* [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops
@ 2026-08-29 2:39 Steven Rostedt
2026-08-29 2:55 ` sashiko-bot
2026-09-01 19:34 ` Breno Leitao
0 siblings, 2 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-08-29 2:39 UTC (permalink / raw)
To: LKML, Linux trace kernel
Cc: Masami Hiramatsu, Mathieu Desnoyers, Mark Rutland, Breno Leitao
From: Steven Rostedt <rostedt@goodmis.org>
The trace instance files set_ftrace_filter and set_ftrace_notrace was
updated to work with specific trace instances (trace_arrays). The issue is
that when these files are opened, there is a small race window where it
will use the ftrace_ops from the inode->private pointer to get a reference
to the trace_array and then take its reference. The problem is that the
ftrace_ops itself could be freed. If the rmdir on the instance happens at
the same time the set_ftrace_filter file is opened, the rmdir could have
also freed the ftrace_ops and referencing it will cause a use-after-free
bug and crash the kernel.
Instead, pass in the trace_array as the file private data (NULL for the
top level instance), and then pass both the trace_array and the ftrace_ops
to the ftrace_regex_open() function. If the trace_array is NULL, then it
just uses the ftrace_ops without the need to take its reference (like
normal). If the ftrace_ops is NULL, that is only the case for the top
level instance and the global_ops can be used.
This allows the trace_array to have its reference incremented before
touching the ftrace_ops that could also be freed when the instance is.
Cc: stable@vger.kernel.org
Fixes: 591dffdade9f0 ("ftrace: Allow for function tracing instance to filter functions")
Reported-by: Breno Leitao <leitao@debian.org>
Closes: https://lore.kernel.org/all/apGORjltZgAiAYHT@gmail.com/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
Changes since v1: https://lore.kernel.org/all/20260828155942.445f46e5@gandalf.local.home/
- Always call tracing_check_open_get_tr()
- Still use ops->private from global_ops if both tr and ops are NULL
(Sashiko)
include/linux/ftrace.h | 5 +--
kernel/trace/ftrace.c | 57 ++++++++++++++++++++++------------
kernel/trace/trace.h | 5 +--
kernel/trace/trace_functions.c | 2 +-
kernel/trace/trace_stack.c | 2 +-
5 files changed, 45 insertions(+), 26 deletions(-)
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 02bc5027523a..bd76a16a63af 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -866,8 +866,9 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec);
unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec);
extern ftrace_func_t ftrace_trace_function;
+struct trace_array;
-int ftrace_regex_open(struct ftrace_ops *ops, int flag,
+int ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag,
struct inode *inode, struct file *file);
ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf,
size_t cnt, loff_t *ppos);
@@ -1077,7 +1078,7 @@ static inline unsigned long ftrace_location(unsigned long ip)
* have them defined when ftrace is not enabled, but these
* functions may still be called. Use a macro instead of inline.
*/
-#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; })
+#define ftrace_regex_open(tr, ops, flag, inode, file) ({ -ENODEV; })
#define ftrace_set_early_filter(ops, buf, enable) do { } while (0)
#define ftrace_set_filter_ip(ops, ip, remove, reset) ({ -ENODEV; })
#define ftrace_set_filter_ips(ops, ips, cnt, remove, reset) ({ -ENODEV; })
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f9d80c7bd9f1..c7cf36f2dd7b 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -4677,7 +4677,8 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file)
/**
* ftrace_regex_open - initialize function tracer filter files
- * @ops: The ftrace_ops that hold the hash filters
+ * @tr: The trace_array that holds the ftrace_ops [optional]
+ * @ops: The ftrace_ops that hold the hash filters [optional]
* @flag: The type of filter to process
* @inode: The inode, usually passed in to your open routine
* @file: The file, usually passed in to your open routine
@@ -4691,26 +4692,45 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file)
* tracing_lseek() should be used as the lseek routine, and
* release must call ftrace_regex_release().
*
+ * Note, If @tr is not NULL, its reference has to be taken before
+ * @ops may be referenced.
+ * If @ops is NULL and @tr is not, then @tr->ops is used.
+ * If @tr is NULL and @ops is not then @ops->private is uesd for @tr.
+ * If both @tr and @ops are NULL, then the &global_ops is
+ * to be used, and @tr will be the global_ops.private pointer.
+ *
* Returns: 0 on success or a negative errno value on failure
*/
int
-ftrace_regex_open(struct ftrace_ops *ops, int flag,
+ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag,
struct inode *inode, struct file *file)
{
- struct ftrace_iterator *iter;
+ struct ftrace_iterator *iter = NULL;
struct ftrace_hash *hash;
struct list_head *mod_head;
- struct trace_array *tr = ops->private;
- int ret = -ENOMEM;
-
- ftrace_ops_init(ops);
+ int ret = -ENODEV;
if (unlikely(ftrace_disabled))
return -ENODEV;
+ if (!tr) {
+ if (!ops)
+ ops = &global_ops;
+ tr = ops->private;
+ }
+
if (tracing_check_open_get_tr(tr))
return -ENODEV;
+ if (!ops)
+ ops = tr->ops;
+
+ if (WARN_ON_ONCE(!ops))
+ goto out;
+
+ ftrace_ops_init(ops);
+
+ ret = -ENOMEM;
iter = kzalloc_obj(*iter);
if (!iter)
goto out;
@@ -4788,21 +4808,19 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
static int
ftrace_filter_open(struct inode *inode, struct file *file)
{
- struct ftrace_ops *ops = inode->i_private;
+ struct trace_array *tr = inode->i_private;
- /* Checks for tracefs lockdown */
- return ftrace_regex_open(ops,
- FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES,
- inode, file);
+ return ftrace_regex_open(tr, NULL,
+ FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES,
+ inode, file);
}
static int
ftrace_notrace_open(struct inode *inode, struct file *file)
{
- struct ftrace_ops *ops = inode->i_private;
+ struct trace_array *tr = inode->i_private;
- /* Checks for tracefs lockdown */
- return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE,
+ return ftrace_regex_open(tr, NULL, FTRACE_ITER_NOTRACE,
inode, file);
}
@@ -7492,15 +7510,15 @@ static const struct file_operations ftrace_graph_notrace_fops = {
};
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
-void ftrace_create_filter_files(struct ftrace_ops *ops,
+void ftrace_create_filter_files(struct trace_array *tr,
struct dentry *parent)
{
trace_create_file("set_ftrace_filter", TRACE_MODE_WRITE, parent,
- ops, &ftrace_filter_fops);
+ tr, &ftrace_filter_fops);
trace_create_file("set_ftrace_notrace", TRACE_MODE_WRITE, parent,
- ops, &ftrace_notrace_fops);
+ tr, &ftrace_notrace_fops);
}
/*
@@ -7525,7 +7543,6 @@ void ftrace_destroy_filter_files(struct ftrace_ops *ops)
static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer)
{
-
trace_create_file("available_filter_functions", TRACE_MODE_READ,
d_tracer, NULL, &ftrace_avail_fops);
@@ -7538,7 +7555,7 @@ static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer)
trace_create_file("touched_functions", TRACE_MODE_READ,
d_tracer, NULL, &ftrace_touched_fops);
- ftrace_create_filter_files(&global_ops, d_tracer);
+ ftrace_create_filter_files(NULL, d_tracer);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
trace_create_file("set_graph_function", TRACE_MODE_WRITE, d_tracer,
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 74a7a50d1e78..3c111ca88e32 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1340,7 +1340,7 @@ extern void clear_ftrace_function_probes(struct trace_array *tr);
int register_ftrace_command(struct ftrace_func_command *cmd);
int unregister_ftrace_command(struct ftrace_func_command *cmd);
-void ftrace_create_filter_files(struct ftrace_ops *ops,
+void ftrace_create_filter_files(struct trace_array *tr,
struct dentry *parent);
void ftrace_destroy_filter_files(struct ftrace_ops *ops);
@@ -1363,11 +1363,12 @@ static inline void clear_ftrace_function_probes(struct trace_array *tr)
{
}
+static inline void ftrace_create_filter_files(struct trace_array *tr,
+ struct dentry *parent) { }
/*
* The ops parameter passed in is usually undefined.
* This must be a macro.
*/
-#define ftrace_create_filter_files(ops, parent) do { } while (0)
#define ftrace_destroy_filter_files(ops) do { } while (0)
#endif /* CONFIG_FUNCTION_TRACER && CONFIG_DYNAMIC_FTRACE */
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index cd37f2013758..c879d43a5fbb 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -101,7 +101,7 @@ int ftrace_create_function_files(struct trace_array *tr,
return ret;
}
- ftrace_create_filter_files(tr->ops, parent);
+ ftrace_create_filter_files(tr, parent);
return 0;
}
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index 0aa2514a6593..e7f4e523587d 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -499,7 +499,7 @@ stack_trace_filter_open(struct inode *inode, struct file *file)
struct ftrace_ops *ops = inode->i_private;
/* Checks for tracefs lockdown */
- return ftrace_regex_open(ops, FTRACE_ITER_FILTER,
+ return ftrace_regex_open(NULL, ops, FTRACE_ITER_FILTER,
inode, file);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops
2026-08-29 2:39 [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops Steven Rostedt
@ 2026-08-29 2:55 ` sashiko-bot
2026-08-30 0:51 ` Steven Rostedt
2026-09-01 19:34 ` Breno Leitao
1 sibling, 1 reply; 4+ messages in thread
From: sashiko-bot @ 2026-08-29 2:55 UTC (permalink / raw)
To: Steven Rostedt; +Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] ABA problem in trace_array lookup allows a file descriptor to attach to a newly created trace instance instead of failing.
Pre-existing issues:
- [High] If `allocate_fgraph_ops` fails in `ftrace_create_function_files`, `tr->ops` is freed but not set to `NULL`. During instance removal, this dangling pointer causes a Use-After-Free and Double Free.
- [High] Concurrent lockless calls to `ftrace_ops_init` can lead to double `mutex_init` and list corruption.
--
commit f37f9bc71afd70bff68b27fdd3afcdd0c07c7ae7
Author: Steven Rostedt <rostedt@goodmis.org>
ftrace: Take trace_array reference before accessing its ftrace_ops
The trace instance files set_ftrace_filter and set_ftrace_notrace was
updated to work with specific trace instances (trace_arrays). This patch
changes the files to pass the trace_array as the file private data,
allowing the trace_array reference to be incremented before touching
the ftrace_ops.
Fixes: 591dffdade9f0 ("ftrace: Allow for function tracing instance to filter functions")
Closes: https://lore.kernel.org/all/apGORjltZgAiAYHT@gmail.com/
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index f9d80c7bd9f16..c7cf36f2dd7be 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
[ ... ]
> @@ -4691,26 +4692,45 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file)
[ ... ]
> int
> -ftrace_regex_open(struct ftrace_ops *ops, int flag,
> +ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag,
> struct inode *inode, struct file *file)
> {
[ ... ]
> + if (!ops)
> + ops = tr->ops;
> +
> + if (WARN_ON_ONCE(!ops))
> + goto out;
> +
> + ftrace_ops_init(ops);
[Severity: High]
This is a pre-existing issue, but can concurrent lockless calls to
ftrace_ops_init() lead to double mutex initialization?
In ftrace_regex_open(), this function is called locklessly in the VFS open
path. Inside ftrace_ops_init(), it checks if the initialized flag is set:
kernel/trace/ftrace.c:ftrace_ops_init() {
if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) {
mutex_init(&ops->local_hash.regex_lock);
INIT_LIST_HEAD(&ops->subop_list);
ops->func_hash = &ops->local_hash;
ops->flags |= FTRACE_OPS_FL_INITIALIZED;
}
}
If multiple threads concurrently open the filter file on a newly created
trace instance, both could observe the flag as unset.
Could one thread lock the newly initialized mutex just as the second thread
calls mutex_init() on it, potentially corrupting the mutex wait list?
[ ... ]
> @@ -4788,21 +4808,19 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
> static int
> ftrace_filter_open(struct inode *inode, struct file *file)
> {
> - struct ftrace_ops *ops = inode->i_private;
> + struct trace_array *tr = inode->i_private;
>
> - /* Checks for tracefs lockdown */
> - return ftrace_regex_open(ops,
> - FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES,
> - inode, file);
> + return ftrace_regex_open(tr, NULL,
> + FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES,
> + inode, file);
[Severity: High]
Does this lockless trace_array pointer retrieval introduce an ABA problem?
If a thread pauses after reading tr from inode->i_private, a concurrent
instance removal could free the original trace_array. If a subsequent mkdir
allocates a new trace instance at the exact same memory address, the paused
thread would resume with the old address.
When ftrace_regex_open() calls tracing_check_open_get_tr(tr), it ultimately
validates the pointer in trace_array_get() by comparing the raw pointer
against the global list:
kernel/trace/trace.c:trace_array_get() {
...
list_for_each_entry(this_tr, &ftrace_trace_arrays, list) {
if (tr == this_tr) {
return __trace_array_get(tr);
}
}
}
Because the new instance was allocated at the same address, this comparison
would succeed, incorrectly attaching the file descriptor to the new trace
instance.
Could this lead to cross-instance state corruption?
> }
[ ... ]
> diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
> index cd37f20137581..c879d43a5fbb2 100644
> --- a/kernel/trace/trace_functions.c
> +++ b/kernel/trace/trace_functions.c
> @@ -101,7 +101,7 @@ int ftrace_create_function_files(struct trace_array *tr,
> return ret;
> }
>
> - ftrace_create_filter_files(tr->ops, parent);
> + ftrace_create_filter_files(tr, parent);
>
> return 0;
> }
[Severity: High]
This is a pre-existing issue, but does a failure in allocate_fgraph_ops()
leave a dangling tr->ops pointer?
Just above this hunk in ftrace_create_function_files(), if
allocate_fgraph_ops() fails, it frees tr->ops but doesn't set it to NULL:
kernel/trace/trace_functions.c:ftrace_create_function_files() {
ret = allocate_fgraph_ops(tr, tr->ops);
if (ret) {
kfree(tr->ops);
return ret;
}
}
Since tracefs setup ignores the error, the instance creation succeeds but
leaves a dangling pointer inside the active trace_array.
Later, when the instance is removed, ftrace_destroy_function_files() will call
ftrace_destroy_filter_files(tr->ops) and ftrace_free_ftrace_ops(tr), which might
result in a use-after-free and a double free.
Should tr->ops be set to NULL after kfree(tr->ops)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828223901.29e26edb@robin?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops
2026-08-29 2:55 ` sashiko-bot
@ 2026-08-30 0:51 ` Steven Rostedt
0 siblings, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-08-30 0:51 UTC (permalink / raw)
To: sashiko-bot; +Cc: sashiko-reviews, linux-trace-kernel
On Sat, 29 Aug 2026 02:55:27 +0000
sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
>
> New issues:
> - [High] ABA problem in trace_array lookup allows a file descriptor to attach to a newly created trace instance instead of failing.
>
> Pre-existing issues:
> - [High] If `allocate_fgraph_ops` fails in `ftrace_create_function_files`, `tr->ops` is freed but not set to `NULL`. During instance removal, this dangling pointer causes a Use-After-Free and Double Free.
> - [High] Concurrent lockless calls to `ftrace_ops_init` can lead to double `mutex_init` and list corruption.
> --
>
> commit f37f9bc71afd70bff68b27fdd3afcdd0c07c7ae7
> Author: Steven Rostedt <rostedt@goodmis.org>
>
> ftrace: Take trace_array reference before accessing its ftrace_ops
>
> The trace instance files set_ftrace_filter and set_ftrace_notrace was
> updated to work with specific trace instances (trace_arrays). This patch
> changes the files to pass the trace_array as the file private data,
> allowing the trace_array reference to be incremented before touching
> the ftrace_ops.
>
> Fixes: 591dffdade9f0 ("ftrace: Allow for function tracing instance to filter functions")
> Closes: https://lore.kernel.org/all/apGORjltZgAiAYHT@gmail.com/
>
> > diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> > index f9d80c7bd9f16..c7cf36f2dd7be 100644
> > --- a/kernel/trace/ftrace.c
> > +++ b/kernel/trace/ftrace.c
> [ ... ]
> > @@ -4691,26 +4692,45 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file)
> [ ... ]
> > int
> > -ftrace_regex_open(struct ftrace_ops *ops, int flag,
> > +ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag,
> > struct inode *inode, struct file *file)
> > {
> [ ... ]
> > + if (!ops)
> > + ops = tr->ops;
> > +
> > + if (WARN_ON_ONCE(!ops))
> > + goto out;
> > +
> > + ftrace_ops_init(ops);
>
> [Severity: High]
> This is a pre-existing issue, but can concurrent lockless calls to
> ftrace_ops_init() lead to double mutex initialization?
>
> In ftrace_regex_open(), this function is called locklessly in the VFS open
> path. Inside ftrace_ops_init(), it checks if the initialized flag is set:
>
> kernel/trace/ftrace.c:ftrace_ops_init() {
> if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) {
> mutex_init(&ops->local_hash.regex_lock);
> INIT_LIST_HEAD(&ops->subop_list);
> ops->func_hash = &ops->local_hash;
> ops->flags |= FTRACE_OPS_FL_INITIALIZED;
> }
> }
>
> If multiple threads concurrently open the filter file on a newly created
> trace instance, both could observe the flag as unset.
>
> Could one thread lock the newly initialized mutex just as the second thread
> calls mutex_init() on it, potentially corrupting the mutex wait list?
Hmm, yeah this probably needs some locking to synchronize.
>
> [ ... ]
> > @@ -4788,21 +4808,19 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
> > static int
> > ftrace_filter_open(struct inode *inode, struct file *file)
> > {
> > - struct ftrace_ops *ops = inode->i_private;
> > + struct trace_array *tr = inode->i_private;
> >
> > - /* Checks for tracefs lockdown */
> > - return ftrace_regex_open(ops,
> > - FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES,
> > - inode, file);
> > + return ftrace_regex_open(tr, NULL,
> > + FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES,
> > + inode, file);
>
> [Severity: High]
> Does this lockless trace_array pointer retrieval introduce an ABA problem?
>
> If a thread pauses after reading tr from inode->i_private, a concurrent
> instance removal could free the original trace_array. If a subsequent mkdir
> allocates a new trace instance at the exact same memory address, the paused
> thread would resume with the old address.
>
> When ftrace_regex_open() calls tracing_check_open_get_tr(tr), it ultimately
> validates the pointer in trace_array_get() by comparing the raw pointer
> against the global list:
>
> kernel/trace/trace.c:trace_array_get() {
> ...
> list_for_each_entry(this_tr, &ftrace_trace_arrays, list) {
> if (tr == this_tr) {
> return __trace_array_get(tr);
> }
> }
> }
>
> Because the new instance was allocated at the same address, this comparison
> would succeed, incorrectly attaching the file descriptor to the new trace
> instance.
>
> Could this lead to cross-instance state corruption?
The would at most open the wrong file in the new instance that was
meant for the old instance. It may confuse the administrator doing the
open, but if they are also deleting and creating instances (which are
very big operations that do a lot) and opening one of the files that
they are deleting, then we know they are likely already confused!
As long as it doesn't crash the kernel, this side effect is an issue
for the administrator to not do stupid things or they may get stupid
results.
>
> > }
> [ ... ]
> > diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
> > index cd37f20137581..c879d43a5fbb2 100644
> > --- a/kernel/trace/trace_functions.c
> > +++ b/kernel/trace/trace_functions.c
> > @@ -101,7 +101,7 @@ int ftrace_create_function_files(struct trace_array *tr,
> > return ret;
> > }
> >
> > - ftrace_create_filter_files(tr->ops, parent);
> > + ftrace_create_filter_files(tr, parent);
> >
> > return 0;
> > }
>
> [Severity: High]
> This is a pre-existing issue, but does a failure in allocate_fgraph_ops()
> leave a dangling tr->ops pointer?
>
> Just above this hunk in ftrace_create_function_files(), if
> allocate_fgraph_ops() fails, it frees tr->ops but doesn't set it to NULL:
>
> kernel/trace/trace_functions.c:ftrace_create_function_files() {
> ret = allocate_fgraph_ops(tr, tr->ops);
> if (ret) {
> kfree(tr->ops);
> return ret;
> }
> }
>
> Since tracefs setup ignores the error, the instance creation succeeds but
> leaves a dangling pointer inside the active trace_array.
>
> Later, when the instance is removed, ftrace_destroy_function_files() will call
> ftrace_destroy_filter_files(tr->ops) and ftrace_free_ftrace_ops(tr), which might
> result in a use-after-free and a double free.
>
> Should tr->ops be set to NULL after kfree(tr->ops)?
>
Probably, will investigate.
-- Steve
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops
2026-08-29 2:39 [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops Steven Rostedt
2026-08-29 2:55 ` sashiko-bot
@ 2026-09-01 19:34 ` Breno Leitao
1 sibling, 0 replies; 4+ messages in thread
From: Breno Leitao @ 2026-09-01 19:34 UTC (permalink / raw)
To: Steven Rostedt
Cc: LKML, Linux trace kernel, Masami Hiramatsu, Mathieu Desnoyers,
Mark Rutland
On Fri, Aug 28, 2026 at 10:39:01PM -0400, Steven Rostedt wrote:
> From: Steven Rostedt <rostedt@goodmis.org>
>
> The trace instance files set_ftrace_filter and set_ftrace_notrace was
> updated to work with specific trace instances (trace_arrays). The issue is
> that when these files are opened, there is a small race window where it
> will use the ftrace_ops from the inode->private pointer to get a reference
> to the trace_array and then take its reference. The problem is that the
> ftrace_ops itself could be freed. If the rmdir on the instance happens at
> the same time the set_ftrace_filter file is opened, the rmdir could have
> also freed the ftrace_ops and referencing it will cause a use-after-free
> bug and crash the kernel.
>
> Instead, pass in the trace_array as the file private data (NULL for the
> top level instance), and then pass both the trace_array and the ftrace_ops
> to the ftrace_regex_open() function. If the trace_array is NULL, then it
> just uses the ftrace_ops without the need to take its reference (like
> normal). If the ftrace_ops is NULL, that is only the case for the top
> level instance and the global_ops can be used.
>
> This allows the trace_array to have its reference incremented before
> touching the ftrace_ops that could also be freed when the instance is.
>
> Cc: stable@vger.kernel.org
> Fixes: 591dffdade9f0 ("ftrace: Allow for function tracing instance to filter functions")
> Reported-by: Breno Leitao <leitao@debian.org>
> Closes: https://lore.kernel.org/all/apGORjltZgAiAYHT@gmail.com/
> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Tested-by: Breno Leitao <leitao@debian.org>
Thanks Steven for the quick fix,
--breno
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-01 19:34 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-29 2:39 [PATCH v2] ftrace: Take trace_array reference before accessing its ftrace_ops Steven Rostedt
2026-08-29 2:55 ` sashiko-bot
2026-08-30 0:51 ` Steven Rostedt
2026-09-01 19:34 ` Breno Leitao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox