]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 30 Aug 2024 13:04:18 +0000 (15:04 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 30 Aug 2024 13:04:18 +0000 (15:04 +0200)
added patches:
tracing-have-format-file-honor-event_file_fl_freed.patch

queue-6.6/series
queue-6.6/tracing-have-format-file-honor-event_file_fl_freed.patch [new file with mode: 0644]

index a4bc1b16448468dd917ee9413b7050c2bc00669b..00799486ed28a334568fbac3174ed33fa666d092 100644 (file)
@@ -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 (file)
index 0000000..00ae8d0
--- /dev/null
@@ -0,0 +1,287 @@
+From b1560408692cd0ab0370cfbe9deb03ce97ab3f6d Mon Sep 17 00:00:00 2001
+From: Steven Rostedt <rostedt@goodmis.org>
+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 <rostedt@goodmis.org>
+
+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 <mhiramat@kernel.org>
+Cc: Mathieu Desnoyers   <mathieu.desnoyers@efficios.com>
+Cc: Ajay Kaher <ajay.kaher@broadcom.com>
+Cc: Ilkka Naulapää    <digirigawa@gmail.com>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Al   Viro <viro@zeniv.linux.org.uk>
+Cc: Dan Carpenter   <dan.carpenter@linaro.org>
+Cc: Beau Belgrave <beaub@linux.microsoft.com>
+Cc: Florian Fainelli  <florian.fainelli@broadcom.com>
+Cc: Alexey Makhalov    <alexey.makhalov@broadcom.com>
+Cc: Vasavi Sirnapalli    <vasavi.sirnapalli@broadcom.com>
+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 <minipli@grsecurity.net>
+Tested-by: Mathias Krause <minipli@grsecurity.net>
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+[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 <zhengyejian@huaweicloud.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);