From: Greg Kroah-Hartman Date: Fri, 30 Aug 2024 13:04:18 +0000 (+0200) Subject: 6.6-stable patches X-Git-Tag: v4.19.321~57 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=97aa7cf8344f2c66777f1791de4eaa6f933158ca;p=thirdparty%2Fkernel%2Fstable-queue.git 6.6-stable patches added patches: tracing-have-format-file-honor-event_file_fl_freed.patch --- diff --git a/queue-6.6/series b/queue-6.6/series index a4bc1b16448..00799486ed2 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -24,3 +24,4 @@ selftests-mptcp-join-check-re-re-adding-id-0-endp.patch drm-amdgpu-align-pp_power_profile_mode-with-kernel-docs.patch drm-amdgpu-swsmu-always-force-a-state-reprogram-on-init.patch drm-vmwgfx-fix-prime-with-external-buffers.patch +tracing-have-format-file-honor-event_file_fl_freed.patch diff --git a/queue-6.6/tracing-have-format-file-honor-event_file_fl_freed.patch b/queue-6.6/tracing-have-format-file-honor-event_file_fl_freed.patch new file mode 100644 index 00000000000..00ae8d0fd6c --- /dev/null +++ b/queue-6.6/tracing-have-format-file-honor-event_file_fl_freed.patch @@ -0,0 +1,287 @@ +From b1560408692cd0ab0370cfbe9deb03ce97ab3f6d Mon Sep 17 00:00:00 2001 +From: Steven Rostedt +Date: Tue, 30 Jul 2024 11:06:57 -0400 +Subject: tracing: Have format file honor EVENT_FILE_FL_FREED +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Steven Rostedt + +commit b1560408692cd0ab0370cfbe9deb03ce97ab3f6d upstream. + +When eventfs was introduced, special care had to be done to coordinate the +freeing of the file meta data with the files that are exposed to user +space. The file meta data would have a ref count that is set when the file +is created and would be decremented and freed after the last user that +opened the file closed it. When the file meta data was to be freed, it +would set a flag (EVENT_FILE_FL_FREED) to denote that the file is freed, +and any new references made (like new opens or reads) would fail as it is +marked freed. This allowed other meta data to be freed after this flag was +set (under the event_mutex). + +All the files that were dynamically created in the events directory had a +pointer to the file meta data and would call event_release() when the last +reference to the user space file was closed. This would be the time that it +is safe to free the file meta data. + +A shortcut was made for the "format" file. It's i_private would point to +the "call" entry directly and not point to the file's meta data. This is +because all format files are the same for the same "call", so it was +thought there was no reason to differentiate them. The other files +maintain state (like the "enable", "trigger", etc). But this meant if the +file were to disappear, the "format" file would be unaware of it. + +This caused a race that could be trigger via the user_events test (that +would create dynamic events and free them), and running a loop that would +read the user_events format files: + +In one console run: + + # cd tools/testing/selftests/user_events + # while true; do ./ftrace_test; done + +And in another console run: + + # cd /sys/kernel/tracing/ + # while true; do cat events/user_events/__test_event/format; done 2>/dev/null + +With KASAN memory checking, it would trigger a use-after-free bug report +(which was a real bug). This was because the format file was not checking +the file's meta data flag "EVENT_FILE_FL_FREED", so it would access the +event that the file meta data pointed to after the event was freed. + +After inspection, there are other locations that were found to not check +the EVENT_FILE_FL_FREED flag when accessing the trace_event_file. Add a +new helper function: event_file_file() that will make sure that the +event_mutex is held, and will return NULL if the trace_event_file has the +EVENT_FILE_FL_FREED flag set. Have the first reference of the struct file +pointer use event_file_file() and check for NULL. Later uses can still use +the event_file_data() helper function if the event_mutex is still held and +was not released since the event_file_file() call. + +Link: https://lore.kernel.org/all/20240719204701.1605950-1-minipli@grsecurity.net/ + +Cc: stable@vger.kernel.org +Cc: Masami Hiramatsu +Cc: Mathieu Desnoyers +Cc: Ajay Kaher +Cc: Ilkka Naulapää +Cc: Linus Torvalds +Cc: Al Viro +Cc: Dan Carpenter +Cc: Beau Belgrave +Cc: Florian Fainelli +Cc: Alexey Makhalov +Cc: Vasavi Sirnapalli +Link: https://lore.kernel.org/20240730110657.3b69d3c1@gandalf.local.home +Fixes: b63db58e2fa5d ("eventfs/tracing: Add callback for release of an eventfs_inode") +Reported-by: Mathias Krause +Tested-by: Mathias Krause +Signed-off-by: Steven Rostedt (Google) +[Resolve conflict due to lack of commit a1f157c7a3bb ("tracing: Expand all + ring buffers individually") which add tracing_update_buffers() in +event_enable_write(), that commit is more of a feature than a bugfix +and is not related to the problem fixed by this patch] +Signed-off-by: Zheng Yejian +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace.h | 23 +++++++++++++++++++++++ + kernel/trace/trace_events.c | 33 ++++++++++++++++++++------------- + kernel/trace/trace_events_hist.c | 4 ++-- + kernel/trace/trace_events_inject.c | 2 +- + kernel/trace/trace_events_trigger.c | 6 +++--- + 5 files changed, 49 insertions(+), 19 deletions(-) + +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -1555,6 +1555,29 @@ static inline void *event_file_data(stru + extern struct mutex event_mutex; + extern struct list_head ftrace_events; + ++/* ++ * When the trace_event_file is the filp->i_private pointer, ++ * it must be taken under the event_mutex lock, and then checked ++ * if the EVENT_FILE_FL_FREED flag is set. If it is, then the ++ * data pointed to by the trace_event_file can not be trusted. ++ * ++ * Use the event_file_file() to access the trace_event_file from ++ * the filp the first time under the event_mutex and check for ++ * NULL. If it is needed to be retrieved again and the event_mutex ++ * is still held, then the event_file_data() can be used and it ++ * is guaranteed to be valid. ++ */ ++static inline struct trace_event_file *event_file_file(struct file *filp) ++{ ++ struct trace_event_file *file; ++ ++ lockdep_assert_held(&event_mutex); ++ file = READ_ONCE(file_inode(filp)->i_private); ++ if (!file || file->flags & EVENT_FILE_FL_FREED) ++ return NULL; ++ return file; ++} ++ + extern const struct file_operations event_trigger_fops; + extern const struct file_operations event_hist_fops; + extern const struct file_operations event_hist_debug_fops; +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -1386,12 +1386,12 @@ event_enable_read(struct file *filp, cha + char buf[4] = "0"; + + mutex_lock(&event_mutex); +- file = event_file_data(filp); ++ file = event_file_file(filp); + if (likely(file)) + flags = file->flags; + mutex_unlock(&event_mutex); + +- if (!file || flags & EVENT_FILE_FL_FREED) ++ if (!file) + return -ENODEV; + + if (flags & EVENT_FILE_FL_ENABLED && +@@ -1428,8 +1428,8 @@ event_enable_write(struct file *filp, co + case 1: + ret = -ENODEV; + mutex_lock(&event_mutex); +- file = event_file_data(filp); +- if (likely(file && !(file->flags & EVENT_FILE_FL_FREED))) ++ file = event_file_file(filp); ++ if (likely(file)) + ret = ftrace_event_enable_disable(file, val); + mutex_unlock(&event_mutex); + break; +@@ -1538,7 +1538,8 @@ enum { + + static void *f_next(struct seq_file *m, void *v, loff_t *pos) + { +- struct trace_event_call *call = event_file_data(m->private); ++ struct trace_event_file *file = event_file_data(m->private); ++ struct trace_event_call *call = file->event_call; + struct list_head *common_head = &ftrace_common_fields; + struct list_head *head = trace_get_fields(call); + struct list_head *node = v; +@@ -1570,7 +1571,8 @@ static void *f_next(struct seq_file *m, + + static int f_show(struct seq_file *m, void *v) + { +- struct trace_event_call *call = event_file_data(m->private); ++ struct trace_event_file *file = event_file_data(m->private); ++ struct trace_event_call *call = file->event_call; + struct ftrace_event_field *field; + const char *array_descriptor; + +@@ -1625,12 +1627,14 @@ static int f_show(struct seq_file *m, vo + + static void *f_start(struct seq_file *m, loff_t *pos) + { ++ struct trace_event_file *file; + void *p = (void *)FORMAT_HEADER; + loff_t l = 0; + + /* ->stop() is called even if ->start() fails */ + mutex_lock(&event_mutex); +- if (!event_file_data(m->private)) ++ file = event_file_file(m->private); ++ if (!file) + return ERR_PTR(-ENODEV); + + while (l < *pos && p) +@@ -1704,8 +1708,8 @@ event_filter_read(struct file *filp, cha + trace_seq_init(s); + + mutex_lock(&event_mutex); +- file = event_file_data(filp); +- if (file && !(file->flags & EVENT_FILE_FL_FREED)) ++ file = event_file_file(filp); ++ if (file) + print_event_filter(file, s); + mutex_unlock(&event_mutex); + +@@ -1734,9 +1738,13 @@ event_filter_write(struct file *filp, co + return PTR_ERR(buf); + + mutex_lock(&event_mutex); +- file = event_file_data(filp); +- if (file) +- err = apply_event_filter(file, buf); ++ file = event_file_file(filp); ++ if (file) { ++ if (file->flags & EVENT_FILE_FL_FREED) ++ err = -ENODEV; ++ else ++ err = apply_event_filter(file, buf); ++ } + mutex_unlock(&event_mutex); + + kfree(buf); +@@ -2451,7 +2459,6 @@ static int event_callback(const char *na + if (strcmp(name, "format") == 0) { + *mode = TRACE_MODE_READ; + *fops = &ftrace_event_format_fops; +- *data = call; + return 1; + } + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -5609,7 +5609,7 @@ static int hist_show(struct seq_file *m, + + mutex_lock(&event_mutex); + +- event_file = event_file_data(m->private); ++ event_file = event_file_file(m->private); + if (unlikely(!event_file)) { + ret = -ENODEV; + goto out_unlock; +@@ -5888,7 +5888,7 @@ static int hist_debug_show(struct seq_fi + + mutex_lock(&event_mutex); + +- event_file = event_file_data(m->private); ++ event_file = event_file_file(m->private); + if (unlikely(!event_file)) { + ret = -ENODEV; + goto out_unlock; +--- a/kernel/trace/trace_events_inject.c ++++ b/kernel/trace/trace_events_inject.c +@@ -299,7 +299,7 @@ event_inject_write(struct file *filp, co + strim(buf); + + mutex_lock(&event_mutex); +- file = event_file_data(filp); ++ file = event_file_file(filp); + if (file) { + call = file->event_call; + size = parse_entry(buf, call, &entry); +--- a/kernel/trace/trace_events_trigger.c ++++ b/kernel/trace/trace_events_trigger.c +@@ -159,7 +159,7 @@ static void *trigger_start(struct seq_fi + + /* ->stop() is called even if ->start() fails */ + mutex_lock(&event_mutex); +- event_file = event_file_data(m->private); ++ event_file = event_file_file(m->private); + if (unlikely(!event_file)) + return ERR_PTR(-ENODEV); + +@@ -213,7 +213,7 @@ static int event_trigger_regex_open(stru + + mutex_lock(&event_mutex); + +- if (unlikely(!event_file_data(file))) { ++ if (unlikely(!event_file_file(file))) { + mutex_unlock(&event_mutex); + return -ENODEV; + } +@@ -293,7 +293,7 @@ static ssize_t event_trigger_regex_write + strim(buf); + + mutex_lock(&event_mutex); +- event_file = event_file_data(file); ++ event_file = event_file_file(file); + if (unlikely(!event_file)) { + mutex_unlock(&event_mutex); + kfree(buf);