FAILED: patch "[PATCH] tracing: Take trace_array reference when opening a tracer" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x ed0aff60f83a9bdc2f6556376ac79c96b3ce7e80
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026091636-refined-everyone-e63c@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From ed0aff60f83a9bdc2f6556376ac79c96b3ce7e80 Mon Sep 17 00:00:00 2001
From: Steven Rostedt <[email protected]>
Date: Thu, 10 Sep 2026 22:12:09 -0400
Subject: [PATCH] tracing: Take trace_array reference when opening a tracer
options file
When a tracer option file is opened, it is passed a descriptor that points to an element on the trace_array's topts array. This element has information to find the trace array and other information. It uses this element to take a reference of the trace_array so that the trace_array does not get removed while this file is opened.
Unfortunately, there's a race condition where the element itself could be freed by the removal of the instance the trace_array represents causing a use-after-free as this element that is used to find the trace_array to increment its reference counter is also freed when the instance is removed.
To solve this, add a trace_array_tracer_options_get() helper function that will take the address of the element that is passed to the open function by the inode->i_private pointer and search all the trace_arrays under a lock to find the one that the element's address is in the range of the trace_arrays topts array elements. When a match happens, that trace_array's reference would be increased.
Note, there's a race where if an admin was deleting and creating trace instances at the same time and the memory of the old trace_array's array matched the memory of the new trace_array that it could in theory open the option from the wrong trace array. But we do not care because it would be stupid to perform that kind of action. As long as the only thing that can happen is that the option from the wrong trace array is used and doesn't crash the kernel it will only make the user confused. But if they are doing something stupid like this, they are already confused, so no harm done.
kernel/trace/trace.c +45 -1
@@ -7717,12 +7717,55 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,return cnt; } +static bool tr_option_match (struct trace_array *tr,void *topt) +{ +for (int i =0 ; i < tr->nr_topts; i++) { +struct trace_options *tr_topts = &tr->topts[i]; + +if (topt >= (void *)&tr_topts->topts[0 ] && + topt < (void *)&tr_topts->topts[tr_topts->nr_topts]) +return true; + } +return false; +} + +/* +* The topt is the address of a trace_array->topts[] element that holds the +* the tracer options descriptor. But since the trace_array reference has not +* been taken yet, it cannot be dereferenced as it could have been freed by +* a rmdir of the instance the trace_array represents. +* +* Search the list of trace_arrays and compare the topt to the address of +* the entire trace_array topts array for each trace_array in the list. +* If one is matched, then take the reference and return it. If not, the +* trace_array no longer exits. +*/ +static int trace_array_tracer_options_get (void *topt) +{ +struct trace_array *tr; +int ret; + + ret =security_locked_down (LOCKDOWN_TRACEFS ); +if (ret) +return ret; + +if (tracing_disabled) +return -ENODEV ; + +guard (mutex)(&trace_types_lock); +list_for_each_entry (tr, &ftrace_trace_arrays, list) { +if (tr_option_match (tr, topt)) +return __trace_array_get (tr); + } +return -ENODEV ; +} +static int tracing_open_options (struct inode *inode,struct file *filp) {struct trace_option_dentry *topt = inode->i_private;int ret; - ret =tracing_check_open_get_tr (topt->tr); + ret =trace_array_tracer_options_get (topt);if (ret)return ret;
@@ -7984,6 +8027,7 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer, tr->topts = tr_topts; tr->topts[tr->nr_topts].tracer = tracer; tr->topts[tr->nr_topts].topts = topts; + tr->topts[tr->nr_topts].nr_topts = cnt; tr->nr_topts++;for (cnt =0 ; opts[cnt].name; cnt++) {
kernel/trace/trace.h +1 -0
@@ -227,6 +227,7 @@ struct array_buffer {struct trace_options {struct tracer *tracer;struct trace_option_dentry *topts; +int nr_topts; };struct trace_pid_list *trace_pid_list_alloc (void );
Sasha Levin
maintainerPATCH 1/2 tracing: Clean up use of trace_create_maxlat_file()
From: Steven Rostedt <[email protected]>
[ Upstream commit ba73713da50e5c24499ca8941171593466ea34f7 ]
In trace.c, the function trace_create_maxlat_file() is defined behind the
#ifdef CONFIG_TRACER_MAX_TRACE block. The #else part defines it as:
#define trace_create_maxlat_file(tr, d_tracer) \
trace_create_file("tracing_max_latency", TRACE_MODE_WRITE, \
d_tracer, tr, &tracing_max_lat_fops)But the one place that it it used has:
#ifdef CONFIG_TRACER_MAX_TRACE trace_create_maxlat_file(tr, d_tracer);
#endif
Which is pointless and also wrong!
It only gets created when both CONFIG_TRACE_MAX_TRACE and CONFIG_FS_NOTIFY is defined, but the file itself should not be dependent on CONFIG_FS_NOTIFY. Always create that file when TRACE_MAX_TRACE is defined regardless if FS_NOTIFY is or is not.
Stable-dep-of: ed0aff60f83a ("tracing: Take trace_array reference when opening a tracer options file")
--- kernel/trace/trace.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-)
kernel/trace/trace.c +16 -22
@@ -1690,10 +1690,7 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt)unsigned long __read_mostly tracing_thresh;#ifdef CONFIG_TRACER_MAX_TRACE -static const struct file_operations tracing_max_lat_fops; -#ifdef LATENCY_FS_NOTIFY -static struct workqueue_struct *fsnotify_wq;static void latency_fsnotify_workfn (struct work_struct *work)
@@ -1710,17 +1707,6 @@ static void latency_fsnotify_workfn_irq(struct irq_work *iwork)queue_work (fsnotify_wq, &tr->fsnotify_work); } -static void trace_create_maxlat_file (struct trace_array *tr, -struct dentry *d_tracer) -{ -INIT_WORK (&tr->fsnotify_work, latency_fsnotify_workfn); -init_irq_work (&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq); - tr->d_max_latency =trace_create_file ("tracing_max_latency" , -TRACE_MODE_WRITE , - d_tracer, tr, - &tracing_max_lat_fops); -} -__init static int latency_fsnotify_init (void ) { fsnotify_wq = alloc_workqueue("tr_max_lat_wq",
@@ -1745,14 +1731,22 @@ void latency_fsnotify(struct trace_array *tr)*/ irq_work_queue(&tr->fsnotify_irqwork); } +#endif /* !LATENCY_FS_NOTIFY */ -#else /* !LATENCY_FS_NOTIFY */ - -#define trace_create_maxlat_file (tr, d_tracer) \ - trace_create_file("tracing_max_latency", TRACE_MODE_WRITE, \ - d_tracer, tr, &tracing_max_lat_fops) +static const struct file_operations tracing_max_lat_fops; +static void trace_create_maxlat_file (struct trace_array *tr, +struct dentry *d_tracer) +{ +#ifdef LATENCY_FS_NOTIFY +INIT_WORK (&tr->fsnotify_work, latency_fsnotify_workfn); +init_irq_work (&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq);#endif + tr->d_max_latency =trace_create_file ("tracing_max_latency" , +TRACE_MODE_WRITE , + d_tracer, tr, + &tracing_max_lat_fops); +}/* * Copy the new maximum trace into the separate maximum-trace
@@ -1887,7 +1881,9 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)__update_max_tr (tr, tsk, cpu); arch_spin_unlock(&tr->max_lock); } - +#else /* !CONFIG_TRACER_MAX_TRACE */ +static inline void trace_create_maxlat_file (struct trace_array *tr, +struct dentry *d_tracer) { }#endif /* CONFIG_TRACER_MAX_TRACE */ static int wait_on_pipe (struct trace_iterator *iter,int full)
@@ -9704,9 +9700,7 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)create_trace_options_dir (tr); -#ifdef CONFIG_TRACER_MAX_TRACE trace_create_maxlat_file (tr, d_tracer); -#endif if (ftrace_create_function_files (tr, d_tracer))MEM_FAIL (1 ,"Could not allocate function filter files" );
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 tracing: Take trace_array reference when opening a tracer options file
From: Steven Rostedt <[email protected]>
[ Upstream commit ed0aff60f83a9bdc2f6556376ac79c96b3ce7e80 ]
When a tracer option file is opened, it is passed a descriptor that points to an element on the trace_array's topts array. This element has information to find the trace array and other information. It uses this element to take a reference of the trace_array so that the trace_array does not get removed while this file is opened.
Unfortunately, there's a race condition where the element itself could be freed by the removal of the instance the trace_array represents causing a use-after-free as this element that is used to find the trace_array to increment its reference counter is also freed when the instance is removed.
To solve this, add a trace_array_tracer_options_get() helper function that will take the address of the element that is passed to the open function by the inode->i_private pointer and search all the trace_arrays under a lock to find the one that the element's address is in the range of the trace_arrays topts array elements. When a match happens, that trace_array's reference would be increased.
Note, there's a race where if an admin was deleting and creating trace instances at the same time and the memory of the old trace_array's array matched the memory of the new trace_array that it could in theory open the option from the wrong trace array. But we do not care because it would be stupid to perform that kind of action. As long as the only thing that can happen is that the option from the wrong trace array is used and doesn't crash the kernel it will only make the user confused. But if they are doing something stupid like this, they are already confused, so no harm done.
--- kernel/trace/trace.c | 53 +++++++++++++++++++++++++++++++++++++++++++- kernel/trace/trace.h | 1 + 2 files changed, 53 insertions(+), 1 deletion(-)
kernel/trace/trace.c +52 -1
@@ -8865,12 +8865,62 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,return cnt; } +static bool tr_option_match (struct trace_array *tr,void *topt) +{ +int i; + +for (i =0 ; i < tr->nr_topts; i++) { +struct trace_options *tr_topts = &tr->topts[i]; + +if (topt >= (void *)&tr_topts->topts[0 ] && + topt < (void *)&tr_topts->topts[tr_topts->nr_topts]) +return true; + } +return false; +} + +/* +* The topt is the address of a trace_array->topts[] element that holds the +* the tracer options descriptor. But since the trace_array reference has not +* been taken yet, it cannot be dereferenced as it could have been freed by +* a rmdir of the instance the trace_array represents. +* +* Search the list of trace_arrays and compare the topt to the address of +* the entire trace_array topts array for each trace_array in the list. +* If one is matched, then take the reference and return it. If not, the +* trace_array no longer exits. +*/ +static int trace_array_tracer_options_get (void *topt) +{ +struct trace_array *tr; +int ret; + + ret =security_locked_down (LOCKDOWN_TRACEFS ); +if (ret) +return ret; + +if (tracing_disabled) +return -ENODEV ; + + ret = -ENODEV ; +mutex_lock (&trace_types_lock); +list_for_each_entry (tr, &ftrace_trace_arrays, list) { +if (tr_option_match (tr, topt)) { + tr->ref++; + ret =0 ; +break ; + } + } +mutex_unlock (&trace_types_lock); +return ret; +} +static int tracing_open_options (struct inode *inode,struct file *filp) {struct trace_option_dentry *topt = inode->i_private;int ret; - ret =tracing_check_open_get_tr (topt->tr); + ret =trace_array_tracer_options_get (topt);if (ret)return ret;
@@ -9093,6 +9143,7 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer) tr->topts = tr_topts; tr->topts[tr->nr_topts].tracer = tracer; tr->topts[tr->nr_topts].topts = topts; + tr->topts[tr->nr_topts].nr_topts = cnt; tr->nr_topts++;for (cnt =0 ; opts[cnt].name; cnt++) {
kernel/trace/trace.h +1 -0
@@ -203,6 +203,7 @@ struct array_buffer {struct trace_options {struct tracer *tracer;struct trace_option_dentry *topts; +int nr_topts; };struct trace_pid_list *trace_pid_list_alloc (void );
Signature · 1 line
2.53.0