With the introduction of pluggable BPF monitors, a monitor can be dynamically unregistered while its tracefs files are still open by a userspace process. This leaves a dangling pointer in the tracefs file descriptor's private_data.
Validate that the monitor pointer still exists in the rv_monitors_list before dereferencing it during read/write operations and return an error otherwise. Signed-off-by: Gabriele Monaco <[email protected]> --- kernel/trace/rv/rv.c | 32 +++++++++++++++++++++++++++++--- kernel/trace/rv/rv.h | 1 + kernel/trace/rv/rv_reactors.c | 6 ++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c index 33919c5fbd6a..6ddd3b92da1d 100644 --- a/kernel/trace/rv/rv.c +++ b/kernel/trace/rv/rv.c @@ -245,13 +245,31 @@ struct rv_monitor *rv_get_monitor_by_name(const char *name) /* * This section collects the monitor/ files and folders. */ + +bool rv_is_monitor_registered(struct rv_monitor *mon) +{ + struct rv_monitor *m; + + list_for_each_entry_rcu(m, &rv_monitors_list, list, + lockdep_is_held(&rv_interface_lock)) { + if (m == mon) + return true; + } + return false; +} + static ssize_t monitor_enable_read_data(struct file *filp, char __user *user_buf, size_t count, loff_t *ppos) { struct rv_monitor *mon = filp->private_data; const char *buff; - buff = mon->enabled ? "1\n" : "0\n"; + scoped_guard(rcu) { + if (!rv_is_monitor_registered(mon)) + return -ENODEV; + + buff = mon->enabled ? "1\n" : "0\n"; + } return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1); } @@ -384,6 +402,9 @@ static ssize_t monitor_enable_write_data(struct file *filp, const char __user *u guard(mutex)(&rv_interface_lock); + if (!rv_is_monitor_registered(mon)) + return -ENODEV; + if (val) retval = rv_enable_monitor(mon); else @@ -407,9 +428,14 @@ static ssize_t monitor_desc_read_data(struct file *filp, char __user *user_buf, struct rv_monitor *mon = filp->private_data; char buff[MAX_RV_DESCRIPTION_SIZE + 2]; - memset(buff, 0, sizeof(buff)); + scoped_guard(rcu) { + if (!rv_is_monitor_registered(mon)) + return -ENODEV; - snprintf(buff, sizeof(buff), "%.*s\n", MAX_RV_DESCRIPTION_SIZE, mon->description); + memset(buff, 0, sizeof(buff)); + snprintf(buff, sizeof(buff), "%.*s\n", MAX_RV_DESCRIPTION_SIZE, + mon->description); + } return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff) + 1); } diff --git a/kernel/trace/rv/rv.h b/kernel/trace/rv/rv.h index 4d2c9cb284c9..6650ee9de308 100644 --- a/kernel/trace/rv/rv.h +++ b/kernel/trace/rv/rv.h @@ -28,6 +28,7 @@ int rv_enable_monitor(struct rv_monitor *mon); bool rv_is_container_monitor(struct rv_monitor *mon); bool rv_is_nested_monitor(struct rv_monitor *mon); struct rv_monitor *rv_get_monitor_by_name(const char *name); +bool rv_is_monitor_registered(struct rv_monitor *mon); #ifdef CONFIG_RV_REACTORS int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root); diff --git a/kernel/trace/rv/rv_reactors.c b/kernel/trace/rv/rv_reactors.c index 2f5fc8d18dea..e23e0b5cc0ca 100644 --- a/kernel/trace/rv/rv_reactors.c +++ b/kernel/trace/rv/rv_reactors.c @@ -142,6 +142,9 @@ static int monitor_reactor_show(struct seq_file *m, void *p) struct rv_monitor *mon = m->private; struct rv_reactor *reactor = container_of(p, struct rv_reactor, list); + if (!rv_is_monitor_registered(mon)) + return -ENODEV; + if (mon->reactor == reactor) seq_printf(m, "[%s]\n", reactor->name); else @@ -235,6 +238,9 @@ monitor_reactors_write(struct file *file, const char __user *user_buf, guard(mutex)(&rv_interface_lock); + if (!rv_is_monitor_registered(mon)) + return -ENODEV; + list_for_each_entry(reactor, &rv_reactors_list, list) { if (strcmp(ptr, reactor->name) != 0) continue; -- 2.55.0
