[PATCH OLK-5.10 0/2] Fix instance dir use-after-free in tracefs

This patch series is used to fix uaf in tracefs. Tengda Wu (2): tracing: Avoid use-after-free in tracing_open_file_tr() tracing: Fix illegal address access of trace_event_file in tracing_release_file_tr() kernel/trace/trace.c | 19 ++++++---- kernel/trace/trace_events.c | 32 +++++++++++++++++ kernel/trace/trace_events_hist.c | 62 +++++++++++++++++++++++++++----- 3 files changed, 99 insertions(+), 14 deletions(-) -- 2.34.1

hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBRD5T -------------------------------- There is a race condition in the tracing_open_file_tr() function when obtaining `inode->i_private`. The function __trace_remove_event_dirs() recursively deletes and releases `trace_event_file` (which is `inode->i_private`) and the function tracing_open_file_tr() might be concurrently reading and using it, leading to use-after-free. [instance_rmdir] [event_hist_open] event_trace_del_tracer tracepoint_synchronize_unregister tracing_open_file_tr file = inode->i_private __trace_remove_event_dirs event_put_file(file) tracing_check_open_get_tr(file->tr) Fix this by explicitly setting `inode->i_private` to NULL when deleting those files which ops depend on i_private to obtain trace_event_file, and moving `inode->i_private` inside the event_mutex lock when opening those kind of files. Fixes: 321a6c777dd0 ("tracing: Have trace_event_file have ref counters") Signed-off-by: Tengda Wu <wutengda2@huawei.com> --- kernel/trace/trace.c | 19 +++++++++++++------ kernel/trace/trace_events.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 462d2a70a835..39c37b7d2d12 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4508,14 +4508,21 @@ int tracing_open_generic_tr(struct inode *inode, struct file *filp) */ int tracing_open_file_tr(struct inode *inode, struct file *filp) { - struct trace_event_file *file = inode->i_private; + struct trace_event_file *file; int ret; + mutex_lock(&event_mutex); + file = inode->i_private; + if (!file) { + mutex_unlock(&event_mutex); + return -ENODEV; + } + ret = tracing_check_open_get_tr(file->tr); - if (ret) + if (ret) { + mutex_unlock(&event_mutex); return ret; - - mutex_lock(&event_mutex); + } /* Fail if the file is marked for removal */ if (file->flags & EVENT_FILE_FL_FREED) { @@ -4529,14 +4536,14 @@ int tracing_open_file_tr(struct inode *inode, struct file *filp) if (ret) return ret; - filp->private_data = inode->i_private; + filp->private_data = file; return 0; } int tracing_release_file_tr(struct inode *inode, struct file *filp) { - struct trace_event_file *file = inode->i_private; + struct trace_event_file *file = filp->private_data; trace_array_put(file->tr); event_file_put(file); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c9ee8b730e43..32b5d043203a 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -764,9 +764,41 @@ void event_file_put(struct trace_event_file *file) } } +static bool file_op_depends_on_i_priv(const struct dentry *dentry) +{ + const struct file_operations *fop = NULL; + + if (!d_really_is_positive(dentry) || !d_inode(dentry)->i_fop) + return false; + + fop = d_inode(dentry)->i_fop; + if (fop->open == tracing_open_file_tr) + return true; +#ifdef CONFIG_HIST_TRIGGERS + if (fop == &event_hist_fops) + return true; +#endif +#ifdef CONFIG_HIST_TRIGGERS_DEBUG + if (fop == &event_hist_debug_fops) + return true; +#endif + + return false; +} + static void remove_event_file_dir(struct trace_event_file *file) { struct dentry *dir = file->dir; + struct dentry *child; + + if (dir) { + spin_lock(&dir->d_lock); /* probably unneeded */ + list_for_each_entry(child, &dir->d_subdirs, d_child) { + if (file_op_depends_on_i_priv(child)) + d_inode(child)->i_private = NULL; + } + spin_unlock(&dir->d_lock); + } tracefs_remove(dir); -- 2.34.1

hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBRD5T -------------------------------- In handling concurrency issues with instance_rmdir, the previous commit uses `file->private_data` to pass `trace_event_file` object between file open and close. However, this approach is ineffective for "hist" file because when event_hist_open() is called, `file->private_data` is reassigned to `seq_file`, causing the loss of `trace_event_file`. Consequently, during tracing_single_release_file_tr(), an error occurs where `seq_file` is mistakenly treated as `trace_event_file`, leading to illegal address access. To fix it, a new struct `hist_file_data` is introduced to hold both `struct file` and `struct trace_event_file`. During event_hist_open(), `hist_file_data` is stored in `seq_file->private`. During event_hist_release(), `trace_event_file` is retrieved from `seq_file->private` and released. Due to changes in `seq_file->private` data, adjustments need to be made at locations within "hist" that originally used `seq_file->private`. Fixes: 3e663e6829c9 ("tracing: Avoid use-after-free in tracing_open_file_tr()") Signed-off-by: Tengda Wu <wutengda2@huawei.com> --- kernel/trace/trace_events_hist.c | 62 +++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 920588dfdeb4..532e4dbc98e6 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -4794,15 +4794,21 @@ static void hist_trigger_show(struct seq_file *m, n_entries, (u64)atomic64_read(&hist_data->map->drops)); } +struct hist_file_data { + struct file *file; + struct trace_event_file *event_file; +}; + static int hist_show(struct seq_file *m, void *v) { + struct hist_file_data *hist_file = m->private; struct event_trigger_data *data; struct trace_event_file *event_file; int n = 0, ret = 0; mutex_lock(&event_mutex); - event_file = event_file_data(m->private); + event_file = event_file_data(hist_file->file); if (unlikely(!event_file)) { ret = -ENODEV; goto out_unlock; @@ -4819,24 +4825,50 @@ static int hist_show(struct seq_file *m, void *v) return ret; } +static int tracing_release_hist_file_tr(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + struct hist_file_data *hist_file = m->private; + struct trace_event_file *event_file = hist_file->event_file; + + trace_array_put(event_file->tr); + event_file_put(event_file); + kfree(hist_file); + + return single_release(inode, file); +} + static int event_hist_open(struct inode *inode, struct file *file) { + struct hist_file_data *hist_file; int ret; + hist_file = kzalloc(sizeof(*hist_file), GFP_KERNEL); + if (!hist_file) + return -ENOMEM; + ret = tracing_open_file_tr(inode, file); - if (ret) + if (ret) { + kfree(hist_file); return ret; + } + + hist_file->file = file; + hist_file->event_file = file->private_data; /* Clear private_data to avoid warning in single_open() */ file->private_data = NULL; - return single_open(file, hist_show, file); + ret = single_open(file, hist_show, hist_file); + if (ret) + kfree(hist_file); + return ret; } const struct file_operations event_hist_fops = { .open = event_hist_open, .read = seq_read, .llseek = seq_lseek, - .release = tracing_single_release_file_tr, + .release = tracing_release_hist_file_tr, }; #ifdef CONFIG_HIST_TRIGGERS_DEBUG @@ -5070,13 +5102,14 @@ static void hist_trigger_debug_show(struct seq_file *m, static int hist_debug_show(struct seq_file *m, void *v) { + struct hist_file_data *hist_file = m->private; struct event_trigger_data *data; struct trace_event_file *event_file; int n = 0, ret = 0; mutex_lock(&event_mutex); - event_file = event_file_data(m->private); + event_file = event_file_data(hist_file->file); if (unlikely(!event_file)) { ret = -ENODEV; goto out_unlock; @@ -5095,22 +5128,35 @@ static int hist_debug_show(struct seq_file *m, void *v) static int event_hist_debug_open(struct inode *inode, struct file *file) { + struct hist_file_data *hist_file; int ret; + hist_file = kzalloc(sizeof(*hist_file), GFP_KERNEL); + if (!hist_file) + return -ENOMEM; + ret = tracing_open_file_tr(inode, file); - if (ret) + if (ret) { + kfree(hist_file); return ret; + } + + hist_file->file = file; + hist_file->event_file = file->private_data; /* Clear private_data to avoid warning in single_open() */ file->private_data = NULL; - return single_open(file, hist_debug_show, file); + ret = single_open(file, hist_debug_show, hist_file); + if (ret) + kfree(hist_file); + return ret; } const struct file_operations event_hist_debug_fops = { .open = event_hist_debug_open, .read = seq_read, .llseek = seq_lseek, - .release = tracing_single_release_file_tr, + .release = tracing_release_hist_file_tr, }; #endif -- 2.34.1

反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/15606 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DTN... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/15606 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DTN...
participants (2)
-
patchwork bot
-
Tengda Wu