* [PATCH v5 5/5] perf: enable unprivileged syscall tracing with perf trace
[not found] <20260714183150.292861-2-ashelat@redhat.com>
@ 2026-07-14 18:31 ` Anubhav Shelat
0 siblings, 0 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-14 18:31 UTC (permalink / raw)
To: mpetlan, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, linux-perf-users,
linux-kernel, linux-trace-kernel
Cc: Anubhav Shelat
Allow unprivileged users to trace their own processes' syscalls using
perf trace, similar to strace without the overhead of ptrace().
Currently, perf trace requires CAP_PERFMON or paranoid level ≤ 1 even
though the kernel has existing infrastructure (TRACE_EVENT_FL_CAP_ANY)
designed to mark syscall tracepoints as safe for unprivileged access.
To fix this:
1. Loosen the condition in perf_event_open() which requires privileges
for all events with exclude_kernel=0. This allows perf_event_open() to
bypass the paranoid check for task-attached tracepoint events. Ensure
that sample types which can expose kernel addresses to unprivileged
users are blocked. Ensure the PERF_SECURITY_KERNEL LSM hook is
preserved.
2. Add a check to perf_trace_event_perm() to block PERF_SAMPLE_IP on
kernel tracepoints for unprivileged users to prevent KASLR bypass. We do
this here rather than in kaddr_leak because perf_trace_event_perm() can
distinguish between kernel tracepoints and uprobe tracepoints, where the
IP is a safe user space address and is necessary for uprobe
functionality.
3. Restrict pure counting events (no PERF_SAMPLE_RAW) to
TRACE_EVENT_FL_CAP_ANY tracepoints preventing unprivileged users from
counting internal kernel tracepoints while preserving current
behavior for exclude_kernel=1 events.
Example usage after this change:
$ perf trace ls # works as unprivileged user
$ perf trace # system-wide, still requires privileges
$ perf trace -p 1234 # requires ptrace permission on pid 1234
Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
kernel/events/core.c | 28 +++++++++++++++++++++++++---
kernel/trace/trace_event_perf.c | 28 +++++++++++++++++++++++++++-
2 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 954c36e28101..48bfff07ae02 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -13910,9 +13910,31 @@ SYSCALL_DEFINE5(perf_event_open,
return err;
if (!attr.exclude_kernel) {
- err = perf_allow_kernel();
- if (err)
- return err;
+ bool tp_bypass = false;
+
+ /* Check unprivileged tracepoints */
+ if (attr.type == PERF_TYPE_TRACEPOINT && pid != -1) {
+ /*
+ * Block sample types that expose kernel addresses to
+ * prevent KASLR bypass
+ */
+ u64 kaddr_leak = PERF_SAMPLE_CALLCHAIN |
+ PERF_SAMPLE_BRANCH_STACK |
+ PERF_SAMPLE_ADDR |
+ PERF_SAMPLE_REGS_INTR;
+
+ tp_bypass = !(attr.sample_type & kaddr_leak);
+ }
+
+ if (!tp_bypass) {
+ err = perf_allow_kernel();
+ if (err)
+ return err;
+ } else {
+ err = security_perf_event_open(PERF_SECURITY_KERNEL);
+ if (err)
+ return err;
+ }
}
if (attr.namespaces) {
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 5b272856e5ab..a264154b460e 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -24,6 +24,16 @@ typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)])
/* Count the events in use (per event id, not per instance) */
static int total_ref_count;
+/* Check if perf tracepoint is restricted for unprivileged users */
+static bool perf_tp_is_restricted(struct perf_event *p_event)
+{
+ if (p_event->attr.exclude_kernel)
+ return false;
+ if (sysctl_perf_event_paranoid <= 1 || perfmon_capable())
+ return false;
+ return true;
+}
+
static int perf_trace_event_perm(struct trace_event_call *tp_event,
struct perf_event *p_event)
{
@@ -72,9 +82,25 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event,
return -EINVAL;
}
+ /*
+ * PERF_SAMPLE_IP on kernel tracepoints exposes a kernel text
+ * address, weakening KASLR. Block for unprivileged users unless
+ * the tracepoint is a uprobe (userspace IP, safe to expose).
+ */
+ if ((p_event->attr.sample_type & PERF_SAMPLE_IP) &&
+ !(tp_event->flags & TRACE_EVENT_FL_UPROBE) &&
+ perf_tp_is_restricted(p_event))
+ return -EACCES;
+
/* No tracing, just counting, so no obvious leak */
- if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
+ if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) {
+ /* Prevent unprivileged users from counting kernel tracepoints */
+ if (perf_tp_is_restricted(p_event) &&
+ !(p_event->attach_state == PERF_ATTACH_TASK &&
+ (tp_event->flags & TRACE_EVENT_FL_CAP_ANY)))
+ return -EACCES;
return 0;
+ }
/* Some events are ok to be traced by non-root users... */
if (p_event->attach_state == PERF_ATTACH_TASK) {
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 0/5] Enable perf tracing for unprivileged users
@ 2026-07-15 13:52 Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 1/5] eventfs: define event fields before directory creation Anubhav Shelat
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
To: rostedt, acme, peterz
Cc: linux-kernel, linux-trace-kernel, linux-perf-users,
Anubhav Shelat
Enable users to use perf-trace to trace their own processes, like strace
but without the overhead of ptrace(). Ensure that users cannot access
other users' or systemwide tracing data.
Changes in v5:
- Move event_define_fields() before directory creation. If
event_define_fields() fails then we don't need to cleanup whatever
dirs were created.
- New read-only eventfs file system with the same structure as
/sys/kernel/tracing/events/ to handle files read by unprivileged
users.
- Allow unprivileged users to fall back to /sys/kernel/events/ when they
cannot access /sys/kernel/tracing/events/.
- Factor out reused code into helper function that checks if a
tracepoint should be restricted in commit 5.
Changes in v4:
- Preserve security_perf_event_open(PERF_SECURITY_KERNEL) LSM hook in
the tp_bypass path.
- Lift the PERF_SAMPLE_IP check out of the tp_bypass path above the
PERF_SAMPLE_RAW branch so it applies to counting and sampling. This
also allows us to ensure PERF_SAMPLE_IP is set for uprobes.
- Block counting path for TRACE_EVENT_FL_CAP_ANY for unprivileged users
with sysctl_perf_event_paranoid > 1.
Changes in v3:
- Don't set PERF_SAMPLE_IP for unprivileged tracepoints. This allows us
to exclude PERF_SAMPLE_IP from kaddr_leak without weakening KASLR.
- Mount tracefs as world-traversable so users can access eventfs
directories.
Anubhav Shelat (5):
eventfs: define event fields before directory creation
tracefs: add read-only eventfs filesystem at /sys/kernel/events
perf tools: fall back to eventfs for unprivileged event discovery
perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints
perf: enable unprivileged syscall tracing with perf trace
fs/tracefs/event_inode.c | 61 ++++++++++++++++++
fs/tracefs/inode.c | 95 ++++++++++++++++++++++++++-
fs/tracefs/internal.h | 3 +
include/linux/trace_events.h | 1 +
include/linux/tracefs.h | 4 ++
include/uapi/linux/magic.h | 1 +
kernel/events/core.c | 28 +++++++-
kernel/trace/trace.h | 2 +
kernel/trace/trace_event_perf.c | 28 +++++++-
kernel/trace/trace_events.c | 100 +++++++++++++++++++++++++++--
tools/lib/api/fs/fs.c | 10 +++
tools/lib/api/fs/fs.h | 1 +
tools/lib/api/fs/tracing_path.c | 52 +++++++++++++--
tools/lib/api/fs/tracing_path.h | 1 +
tools/perf/util/evsel.c | 14 +++-
tools/perf/util/tp_pmu.c | 5 +-
tools/perf/util/trace-event-info.c | 19 +++---
17 files changed, 395 insertions(+), 30 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v5 1/5] eventfs: define event fields before directory creation
2026-07-15 13:52 [PATCH v5 0/5] Enable perf tracing for unprivileged users Anubhav Shelat
@ 2026-07-15 13:52 ` Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 2/5] tracefs: add read-only eventfs filesystem at /sys/kernel/events Anubhav Shelat
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
To: rostedt, acme, peterz, Masami Hiramatsu, Mathieu Desnoyers,
linux-kernel, linux-trace-kernel
Cc: linux-perf-users, Anubhav Shelat
Move the event_define_fields() call in event_create_dir() before the
eventfs directory creation. Previously, a failure after directory
creation wouldn't clean up eventfs_inode because the error path didn't
call eventfs_remove_dir(). This eliminates the need to clean up the
eventfs directories if event_define_fields() fails.
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
kernel/trace/trace_events.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c46e623e7e0d..ddb6932a3ee7 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3190,6 +3190,13 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
if (WARN_ON_ONCE(strcmp(call->class->system, TRACE_SYSTEM) == 0))
return -ENODEV;
+ ret = event_define_fields(call);
+ if (ret < 0) {
+ pr_warn("Could not initialize trace point events/%s\n",
+ trace_event_name(call));
+ return ret;
+ }
+
e_events = event_subsystem_dir(tr, call->class->system, file, parent);
if (!e_events)
return -ENOMEM;
@@ -3208,12 +3215,6 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
file->ei = ei;
- ret = event_define_fields(call);
- if (ret < 0) {
- pr_warn("Could not initialize trace point events/%s\n", name);
- return ret;
- }
-
/* Gets decremented on freeing of the "enable" file */
event_file_get(file);
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 2/5] tracefs: add read-only eventfs filesystem at /sys/kernel/events
2026-07-15 13:52 [PATCH v5 0/5] Enable perf tracing for unprivileged users Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 1/5] eventfs: define event fields before directory creation Anubhav Shelat
@ 2026-07-15 13:52 ` Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 3/5] perf tools: fall back to eventfs for unprivileged event discovery Anubhav Shelat
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
To: rostedt, acme, peterz, Masami Hiramatsu, Mathieu Desnoyers,
Shivank Garg, Ackerley Tng, Fuad Tabba, Christian Brauner,
Sean Christopherson, Anubhav Shelat, linux-kernel,
linux-trace-kernel
Cc: linux-perf-users
Introduce a new read-only pseudo-filesystem "eventfs" mounted at
/sys/kernel/events that exposes trace event format and id files
(mode 0444) to unprivileged users. This allows tools like perf to
discover event formats without requiring access to the full
tracefs/debugfs mount.
The eventfs filesystem reuses the existing eventfs_inode lazy-lookup
infrastructure. A new set of super_operations
(eventfs_ro_super_operations) shares the tracefs inode allocator so
that eventfs_get_inode() and get_tracefs() work on the RO superblock.
The superblock is manually initialized to ensure the root inode is
allocated with tracefs_alloc_inode, allowing the root to serve
directly as the events directory without another events subdirectory.
Each qualifying event gets a subsystem directory containing format
and id files. The top-level events directory also exposes header_page
and header_event. Similar to tracefs, a change will need to be made in
systemd to mount this filesystem automatically.
Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
fs/tracefs/event_inode.c | 61 +++++++++++++++++++++++
fs/tracefs/inode.c | 95 +++++++++++++++++++++++++++++++++++-
fs/tracefs/internal.h | 3 ++
include/linux/trace_events.h | 1 +
include/linux/tracefs.h | 4 ++
include/uapi/linux/magic.h | 1 +
kernel/trace/trace.h | 2 +
kernel/trace/trace_events.c | 87 +++++++++++++++++++++++++++++++++
8 files changed, 252 insertions(+), 2 deletions(-)
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index 39c7a34531e8..fd6f63ec3ce0 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -812,6 +812,67 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
return ERR_PTR(-ENOMEM);
}
+/**
+ * eventfs_create_events_dir_ro - create a read-only events directory
+ * @name: The name of the top level directory to create.
+ * @entries: A list of entries that represent the files under this directory
+ * @size: The number of @entries
+ * @data: The default data to pass to the files (an entry may override it).
+ *
+ * This function configures the eventfs filesystem root as a read-only
+ * trace event directory using the existing eventfs_inode lazy-lookup
+ * infrastructure.
+ *
+ * See eventfs_create_dir() for use of @entries.
+ */
+struct eventfs_inode *eventfs_create_events_dir_ro(const char *name,
+ const struct eventfs_entry *entries,
+ int size, void *data)
+{
+ struct dentry *dentry;
+ struct eventfs_root_inode *rei;
+ struct eventfs_inode *ei;
+ struct tracefs_inode *ti;
+ struct inode *inode;
+
+ dentry = eventfs_ro_get_root();
+ if (IS_ERR(dentry))
+ return ERR_CAST(dentry);
+
+ inode = d_inode(dentry);
+
+ ei = alloc_root_ei(name);
+ if (!ei)
+ goto fail;
+
+ rei = get_root_inode(ei);
+ rei->events_dir = dentry;
+
+ ei->entries = entries;
+ ei->nr_entries = size;
+ ei->data = data;
+
+ INIT_LIST_HEAD(&ei->children);
+ INIT_LIST_HEAD(&ei->list);
+
+ ti = get_tracefs(inode);
+ ti->flags |= TRACEFS_EVENT_INODE;
+ ti->private = ei;
+
+ inode->i_op = &eventfs_dir_inode_operations;
+ inode->i_fop = &eventfs_file_operations;
+
+ dentry->d_fsdata = get_ei(ei);
+
+ return ei;
+
+ fail:
+ cleanup_ei(ei);
+ dput(dentry);
+ eventfs_ro_put_root();
+ return ERR_PTR(-ENOMEM);
+}
+
/**
* eventfs_remove_rec - remove eventfs dir or file from list
* @ei: eventfs_inode to be removed.
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index f3d6188a3b7b..fd064d79d940 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -30,6 +30,9 @@ static struct vfsmount *tracefs_mount;
static int tracefs_mount_count;
static bool tracefs_registered;
+static struct vfsmount *eventfs_ro_mount;
+static int eventfs_ro_mount_count;
+
/*
* Keep track of all tracefs_inodes in order to update their
* flags if necessary on a remount.
@@ -423,6 +426,14 @@ static const struct super_operations tracefs_super_operations = {
.show_options = tracefs_show_options,
};
+static const struct super_operations eventfs_ro_super_operations = {
+ .alloc_inode = tracefs_alloc_inode,
+ .free_inode = tracefs_free_inode,
+ .destroy_inode = tracefs_destroy_inode,
+ .drop_inode = tracefs_drop_inode,
+ .statfs = simple_statfs,
+};
+
/*
* It would be cleaner if eventfs had its own dentry ops.
*
@@ -523,6 +534,79 @@ static struct file_system_type trace_fs_type = {
};
MODULE_ALIAS_FS("tracefs");
+static int eventfs_ro_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+ struct inode *inode;
+ struct dentry *root;
+
+ sb->s_blocksize = PAGE_SIZE;
+ sb->s_blocksize_bits = PAGE_SHIFT;
+ sb->s_magic = EVENTFS_SUPER_MAGIC;
+ sb->s_op = &eventfs_ro_super_operations;
+ sb->s_time_gran = 1;
+ sb->s_flags |= SB_RDONLY;
+
+ inode = new_inode(sb);
+ if (!inode)
+ return -ENOMEM;
+
+ inode->i_ino = 1;
+ inode->i_mode = S_IFDIR | 0555;
+ simple_inode_init_ts(inode);
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ set_nlink(inode, 2);
+
+ set_default_d_op(sb, &tracefs_dentry_operations);
+
+ root = d_make_root(inode);
+ if (!root)
+ return -ENOMEM;
+
+ sb->s_root = root;
+
+ return 0;
+}
+
+static int eventfs_ro_get_tree(struct fs_context *fc)
+{
+ return get_tree_single(fc, eventfs_ro_fill_super);
+}
+
+static const struct fs_context_operations eventfs_ro_context_ops = {
+ .get_tree = eventfs_ro_get_tree,
+};
+
+static int eventfs_ro_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &eventfs_ro_context_ops;
+ return 0;
+}
+
+static struct file_system_type eventfs_ro_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "eventfs",
+ .init_fs_context = eventfs_ro_init_fs_context,
+ .kill_sb = kill_anon_super,
+};
+
+struct dentry *eventfs_ro_get_root(void)
+{
+ int error;
+
+ error = simple_pin_fs(&eventfs_ro_fs_type, &eventfs_ro_mount,
+ &eventfs_ro_mount_count);
+ if (error)
+ return ERR_PTR(error);
+
+ return dget(eventfs_ro_mount->mnt_root);
+}
+
+void eventfs_ro_put_root(void)
+{
+ simple_release_fs(&eventfs_ro_mount, &eventfs_ro_mount_count);
+}
+
struct dentry *tracefs_start_creating(const char *name, struct dentry *parent)
{
struct dentry *dentry;
@@ -801,8 +885,15 @@ static int __init tracefs_init(void)
return -EINVAL;
retval = register_filesystem(&trace_fs_type);
- if (!retval)
- tracefs_registered = true;
+ if (retval)
+ return retval;
+ tracefs_registered = true;
+
+ retval = sysfs_create_mount_point(kernel_kobj, "events");
+ if (retval)
+ return retval;
+
+ retval = register_filesystem(&eventfs_ro_fs_type);
return retval;
}
diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
index a4a7f8431aff..0440413f959b 100644
--- a/fs/tracefs/internal.h
+++ b/fs/tracefs/internal.h
@@ -73,6 +73,9 @@ struct dentry *tracefs_end_creating(struct dentry *dentry);
struct dentry *tracefs_failed_creating(struct dentry *dentry);
struct inode *tracefs_get_inode(struct super_block *sb);
+struct dentry *eventfs_ro_get_root(void);
+void eventfs_ro_put_root(void);
+
void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
void eventfs_d_release(struct dentry *dentry);
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 308c76b57d13..957695fbb015 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -648,6 +648,7 @@ struct trace_event_file {
struct trace_event_call *event_call;
struct event_filter __rcu *filter;
struct eventfs_inode *ei;
+ struct eventfs_inode *ei_ro;
struct trace_array *tr;
struct trace_subsystem_dir *system;
struct list_head triggers;
diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h
index bc354d340046..c175efc51d20 100644
--- a/include/linux/tracefs.h
+++ b/include/linux/tracefs.h
@@ -87,6 +87,10 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
const struct eventfs_entry *entries,
int size, void *data);
+struct eventfs_inode *eventfs_create_events_dir_ro(const char *name,
+ const struct eventfs_entry *entries,
+ int size, void *data);
+
void eventfs_remove_events_dir(struct eventfs_inode *ei);
void eventfs_remove_dir(struct eventfs_inode *ei);
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 4f2da935a76c..7cf8f1a1ae38 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -75,6 +75,7 @@
#define STACK_END_MAGIC 0x57AC6E9D
#define TRACEFS_MAGIC 0x74726163
+#define EVENTFS_SUPER_MAGIC 0x65766673
#define V9FS_MAGIC 0x01021997
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 80fe152af1dd..00c35aaa5069 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -416,6 +416,7 @@ struct trace_array {
struct dentry *options;
struct dentry *percpu_dir;
struct eventfs_inode *event_dir;
+ struct eventfs_inode *event_dir_ro;
struct trace_options *topts;
struct list_head systems;
struct list_head events;
@@ -1604,6 +1605,7 @@ struct trace_subsystem_dir {
struct event_subsystem *subsystem;
struct trace_array *tr;
struct eventfs_inode *ei;
+ struct eventfs_inode *ei_ro;
int ref_count;
int nr_events;
};
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index ddb6932a3ee7..9662cb24a92c 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1279,6 +1279,7 @@ static void remove_subsystem(struct trace_subsystem_dir *dir)
if (!--dir->nr_events) {
eventfs_remove_dir(dir->ei);
+ eventfs_remove_dir(dir->ei_ro);
list_del(&dir->list);
__put_system_dir(dir);
}
@@ -1308,6 +1309,7 @@ void event_file_put(struct trace_event_file *file)
static void remove_event_file_dir(struct trace_event_file *file)
{
eventfs_remove_dir(file->ei);
+ eventfs_remove_dir(file->ei_ro);
list_del(&file->list);
remove_subsystem(file->system);
free_event_filter(file->filter);
@@ -2986,6 +2988,7 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
}
dir->ei = ei;
+ dir->ei_ro = NULL;
dir->tr = tr;
dir->ref_count = 1;
dir->nr_events = 1;
@@ -3126,6 +3129,33 @@ static void event_release(const char *name, void *data)
event_file_put(file);
}
+static int event_callback_ro(const char *name, umode_t *mode, void **data,
+ const struct file_operations **fops)
+{
+ int ret = event_callback(name, mode, data, fops);
+
+ /* Skip writable entries in the read-only tree */
+ if (ret && (*mode & 0222))
+ return 0;
+ if (ret)
+ *mode = 0444;
+ return ret;
+}
+
+static struct eventfs_entry event_entries_ro[] = {
+ {
+ .name = "format",
+ .callback = event_callback_ro,
+ .release = event_release,
+ },
+#ifdef CONFIG_PERF_EVENTS
+ {
+ .name = "id",
+ .callback = event_callback_ro,
+ },
+#endif
+};
+
static int
event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
{
@@ -3218,6 +3248,28 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)
/* Gets decremented on freeing of the "enable" file */
event_file_get(file);
+ /* Create read only eventfs directory */
+ if (!(call->flags & TRACE_EVENT_FL_DYNAMIC) &&
+ !IS_ERR_OR_NULL(tr->event_dir_ro)) {
+ struct trace_subsystem_dir *sdir = file->system;
+
+ if (!sdir->ei_ro) {
+ sdir->ei_ro = eventfs_create_dir(call->class->system,
+ tr->event_dir_ro, NULL, 0, sdir);
+ if (IS_ERR(sdir->ei_ro))
+ sdir->ei_ro = NULL;
+ }
+ if (sdir->ei_ro) {
+ file->ei_ro = eventfs_create_dir(name, sdir->ei_ro,
+ event_entries_ro,
+ ARRAY_SIZE(event_entries_ro), file);
+ if (IS_ERR(file->ei_ro))
+ file->ei_ro = NULL;
+ else
+ event_file_get(file);
+ }
+ }
+
return 0;
}
@@ -4539,10 +4591,35 @@ static int events_callback(const char *name, umode_t *mode, void **data,
return 1;
}
+static int events_callback_ro(const char *name, umode_t *mode, void **data,
+ const struct file_operations **fops)
+{
+ int ret = events_callback(name, mode, data, fops);
+
+ /* Skip writable entries in the read-only tree */
+ if (ret && (*mode & 0222))
+ return 0;
+ if (ret)
+ *mode = 0444;
+ return ret;
+}
+
+static struct eventfs_entry events_entries_ro[] = {
+ {
+ .name = "header_page",
+ .callback = events_callback_ro,
+ },
+ {
+ .name = "header_event",
+ .callback = events_callback_ro,
+ },
+};
+
/* Expects to have event_mutex held when called */
static int
create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
{
+ static bool event_dir_ro_created;
struct eventfs_inode *e_events;
struct dentry *entry;
int nr_entries;
@@ -4596,6 +4673,16 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
tr->event_dir = e_events;
+ if (!event_dir_ro_created && (tr->flags & TRACE_ARRAY_FL_GLOBAL)) {
+ tr->event_dir_ro = eventfs_create_events_dir_ro(
+ "events", events_entries_ro,
+ ARRAY_SIZE(events_entries_ro), tr);
+ if (IS_ERR(tr->event_dir_ro))
+ tr->event_dir_ro = NULL;
+ else
+ event_dir_ro_created = true;
+ }
+
return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 3/5] perf tools: fall back to eventfs for unprivileged event discovery
2026-07-15 13:52 [PATCH v5 0/5] Enable perf tracing for unprivileged users Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 1/5] eventfs: define event fields before directory creation Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 2/5] tracefs: add read-only eventfs filesystem at /sys/kernel/events Anubhav Shelat
@ 2026-07-15 13:52 ` Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 4/5] perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 5/5] perf: enable unprivileged syscall tracing with perf trace Anubhav Shelat
4 siblings, 0 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
To: rostedt, acme, peterz, Ingo Molnar, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Anubhav Shelat, linux-kernel, linux-perf-users
Cc: linux-trace-kernel
When tracefs events are not readable by unprivileged users, fall back
to /sys/kernel/events (the read-only eventfs mount) for tracepoint
format and id file discovery. This allows perf trace to work for
unprivileged users on kernels that expose the eventfs filesystem.
The fallback is transparent: get_events_file() tries the tracefs path
first and only switches to eventfs when access() fails. On kernels
without eventfs, the existing error path is preserved.
Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
tools/lib/api/fs/fs.c | 10 ++++++
tools/lib/api/fs/fs.h | 1 +
tools/lib/api/fs/tracing_path.c | 52 ++++++++++++++++++++++++++----
tools/lib/api/fs/tracing_path.h | 1 +
tools/perf/util/tp_pmu.c | 5 +--
tools/perf/util/trace-event-info.c | 19 ++++++-----
6 files changed, 71 insertions(+), 17 deletions(-)
diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c
index cbd8eab0d1df..abc3581a9703 100644
--- a/tools/lib/api/fs/fs.c
+++ b/tools/lib/api/fs/fs.c
@@ -46,6 +46,10 @@
#define BPF_FS_MAGIC 0xcafe4a11
#endif
+#ifndef EVENTFS_SUPER_MAGIC
+#define EVENTFS_SUPER_MAGIC 0x65766673
+#endif
+
static const char * const sysfs__known_mountpoints[] = {
"/sys",
0,
@@ -88,6 +92,11 @@ static const char * const bpf_fs__known_mountpoints[] = {
0,
};
+static const char * const eventfs__known_mountpoints[] = {
+ "/sys/kernel/events",
+ 0,
+};
+
struct fs {
const char * const name;
const char * const * const mounts;
@@ -150,6 +159,7 @@ FS(debugfs, debugfs, DEBUGFS);
FS(tracefs, tracefs, TRACEFS);
FS(hugetlbfs, hugetlbfs, HUGETLBFS);
FS(bpf_fs, bpf, BPF_FS);
+FS(eventfs, eventfs, EVENTFS_SUPER);
static bool fs__read_mounts(struct fs *fs)
{
diff --git a/tools/lib/api/fs/fs.h b/tools/lib/api/fs/fs.h
index aa222ca30311..57790c93f80e 100644
--- a/tools/lib/api/fs/fs.h
+++ b/tools/lib/api/fs/fs.h
@@ -36,6 +36,7 @@ FS(debugfs)
FS(tracefs)
FS(hugetlbfs)
FS(bpf_fs)
+FS(eventfs)
#undef FS
diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c
index 834fd64c7130..de4ed7c61415 100644
--- a/tools/lib/api/fs/tracing_path.c
+++ b/tools/lib/api/fs/tracing_path.c
@@ -3,6 +3,8 @@
# define _GNU_SOURCE
#endif
+#include <dirent.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -80,13 +82,46 @@ void put_tracing_file(char *file)
free(file);
}
+char *get_events_dir(void)
+{
+ const char *eventfs;
+ char *path;
+ int saved_errno;
+
+ path = get_tracing_file("events");
+ if (path && faccessat(AT_FDCWD, path, R_OK, AT_EACCESS) == 0)
+ return path;
+
+ saved_errno = errno;
+ put_tracing_file(path);
+
+ eventfs = eventfs__mount();
+ if (eventfs && faccessat(AT_FDCWD, eventfs, R_OK, AT_EACCESS) == 0 &&
+ eventfs__configured())
+ return strdup(eventfs);
+
+ /*
+ * Prefer EACCES over other errors: it tells the user that events
+ * exist but are not accessible, which is more actionable than
+ * ENOENT from a missing filesystem.
+ */
+ if (errno != EACCES)
+ errno = saved_errno;
+ return NULL;
+}
+
char *get_events_file(const char *name)
{
- char *file;
+ char *dir, *file;
- if (asprintf(&file, "%s/events/%s", tracing_path_mount(), name) < 0)
+ dir = get_events_dir();
+ if (!dir)
return NULL;
+ if (asprintf(&file, "%s/%s", dir, name) < 0)
+ file = NULL;
+
+ free(dir);
return file;
}
@@ -97,8 +132,8 @@ void put_events_file(char *file)
DIR *tracing_events__opendir(void)
{
+ char *path = get_events_dir();
DIR *dir = NULL;
- char *path = get_tracing_file("events");
if (path) {
dir = opendir(path);
@@ -110,7 +145,7 @@ DIR *tracing_events__opendir(void)
int tracing_events__scandir_alphasort(struct dirent ***namelist)
{
- char *path = get_tracing_file("events");
+ char *path = get_events_dir();
int ret;
if (!path) {
@@ -121,6 +156,9 @@ int tracing_events__scandir_alphasort(struct dirent ***namelist)
ret = scandir(path, namelist, NULL, alphasort);
put_events_file(path);
+ if (ret < 0)
+ *namelist = NULL;
+
return ret;
}
@@ -162,12 +200,12 @@ int tracing_path__strerror_open_tp(int err, char *buf, size_t size,
"Hint:\tIs the debugfs/tracefs filesystem mounted?\n"
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
break;
- case EACCES: {
+ case EACCES:
snprintf(buf, size,
"Error:\tNo permissions to read %s/events/%s\n"
- "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
+ "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n"
+ "Hint:\tOr check if /sys/kernel/events is available\n",
tracing_path, filename, tracing_path_mount());
- }
break;
default:
snprintf(buf, size, "%s", str_error_r(err, sbuf, sizeof(sbuf)));
diff --git a/tools/lib/api/fs/tracing_path.h b/tools/lib/api/fs/tracing_path.h
index fc6347c11deb..a93befb38f80 100644
--- a/tools/lib/api/fs/tracing_path.h
+++ b/tools/lib/api/fs/tracing_path.h
@@ -14,6 +14,7 @@ const char *tracing_path_mount(void);
char *get_tracing_file(const char *name);
void put_tracing_file(char *file);
+char *get_events_dir(void);
char *get_events_file(const char *name);
void put_events_file(char *file);
diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c
index c2be8c9f9084..4f45bd816945 100644
--- a/tools/perf/util/tp_pmu.c
+++ b/tools/perf/util/tp_pmu.c
@@ -6,6 +6,7 @@
#include <api/io_dir.h>
#include <linux/kernel.h>
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
int tp_pmu__id(const char *sys, const char *name)
@@ -15,7 +16,7 @@ int tp_pmu__id(const char *sys, const char *name)
int id, err;
if (!tp_dir)
- return -1;
+ return -errno;
scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
put_events_file(tp_dir);
@@ -66,7 +67,7 @@ int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
struct io_dirent64 *events_ent;
struct io_dir events_dir;
int ret = 0;
- char *events_dir_path = get_tracing_file("events");
+ char *events_dir_path = get_events_dir();
if (!events_dir_path)
return -errno;
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 45774722f249..643451d2c08d 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -92,8 +92,9 @@ static int record_header_files(void)
int err = -EIO;
if (!path) {
+ err = -errno;
pr_debug("can't get tracing/events/header_page");
- return -ENOMEM;
+ return err;
}
if (stat(path, &st) < 0) {
@@ -115,8 +116,8 @@ static int record_header_files(void)
path = get_events_file("header_event");
if (!path) {
+ err = -errno;
pr_debug("can't get tracing/events/header_event");
- err = -ENOMEM;
goto out;
}
@@ -228,13 +229,14 @@ static int record_ftrace_files(struct tracepoint_path *tps)
path = get_events_file("ftrace");
if (!path) {
+ ret = -errno;
pr_debug("can't get tracing/events/ftrace");
- return -ENOMEM;
+ return ret;
}
ret = copy_event_system(path, tps);
- put_tracing_file(path);
+ put_events_file(path);
return ret;
}
@@ -256,15 +258,16 @@ static int record_event_files(struct tracepoint_path *tps)
struct stat st;
char *path;
char *sys;
- DIR *dir;
+ DIR *dir = NULL;
int count = 0;
int ret;
int err;
- path = get_tracing_file("events");
+ path = get_events_dir();
if (!path) {
+ err = -errno;
pr_debug("can't get tracing/events");
- return -ENOMEM;
+ goto out;
}
dir = opendir(path);
@@ -315,7 +318,7 @@ static int record_event_files(struct tracepoint_path *tps)
out:
if (dir)
closedir(dir);
- put_tracing_file(path);
+ put_events_file(path);
return err;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 4/5] perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints
2026-07-15 13:52 [PATCH v5 0/5] Enable perf tracing for unprivileged users Anubhav Shelat
` (2 preceding siblings ...)
2026-07-15 13:52 ` [PATCH v5 3/5] perf tools: fall back to eventfs for unprivileged event discovery Anubhav Shelat
@ 2026-07-15 13:52 ` Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 5/5] perf: enable unprivileged syscall tracing with perf trace Anubhav Shelat
4 siblings, 0 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
To: rostedt, acme, peterz, Ingo Molnar, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Thomas Falcon, linux-perf-users, linux-kernel
Cc: linux-trace-kernel, Anubhav Shelat
For tracepoint events the IP is a static kernel address.
It doesn't vary by sample and provides no useful information for
unprivileged users. Skipping setting PERF_SAMPLE_IP for unprivileged
tracepoints avoids exposing a kernel address that reveals the KASLR base
offset.
Make an exception for uprobes, which are registered as
PERF_TYPE_TRACEPOINT, because the IP is important for their
functionality and is a safe userspace address. Detect them with
__probe_ip (entry) and __probe_ret_ip (return) using evsel__field().
Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
tools/perf/util/evsel.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index ea9fa04429f0..1d85e5503cf3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1571,7 +1571,19 @@ void evsel__config(struct evsel *evsel, const struct record_opts *opts,
attr->write_backward = opts->overwrite ? 1 : 0;
attr->read_format = PERF_FORMAT_LOST;
- evsel__set_sample_bit(evsel, IP);
+ /*
+ * Don't set PERF_SAMPLE_IP for unprivileged kernel tracepoints to
+ * avoid exposing kernel addresses. Uprobes expose only userspace
+ * addresses so they're safe. Detect both entry and return uprobes.
+ */
+ if (attr->type != PERF_TYPE_TRACEPOINT || perf_event_paranoid_check(1)
+#ifdef HAVE_LIBTRACEEVENT
+ || evsel__field(evsel, "__probe_ip")
+ || evsel__field(evsel, "__probe_ret_ip")
+#endif
+ )
+ evsel__set_sample_bit(evsel, IP);
+
evsel__set_sample_bit(evsel, TID);
if (evsel->sample_read) {
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 5/5] perf: enable unprivileged syscall tracing with perf trace
2026-07-15 13:52 [PATCH v5 0/5] Enable perf tracing for unprivileged users Anubhav Shelat
` (3 preceding siblings ...)
2026-07-15 13:52 ` [PATCH v5 4/5] perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints Anubhav Shelat
@ 2026-07-15 13:52 ` Anubhav Shelat
4 siblings, 0 replies; 7+ messages in thread
From: Anubhav Shelat @ 2026-07-15 13:52 UTC (permalink / raw)
To: rostedt, acme, peterz, Ingo Molnar, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Masami Hiramatsu, Mathieu Desnoyers,
linux-perf-users, linux-kernel, linux-trace-kernel
Cc: Anubhav Shelat
Allow unprivileged users to trace their own processes' syscalls using
perf trace, similar to strace without the overhead of ptrace().
Currently, perf trace requires CAP_PERFMON or paranoid level ≤ 1 even
though the kernel has existing infrastructure (TRACE_EVENT_FL_CAP_ANY)
designed to mark syscall tracepoints as safe for unprivileged access.
To fix this:
1. Loosen the condition in perf_event_open() which requires privileges
for all events with exclude_kernel=0. This allows perf_event_open() to
bypass the paranoid check for task-attached tracepoint events. Ensure
that sample types which can expose kernel addresses to unprivileged
users are blocked. Ensure the PERF_SECURITY_KERNEL LSM hook is
preserved.
2. Add a check to perf_trace_event_perm() to block PERF_SAMPLE_IP on
kernel tracepoints for unprivileged users to prevent KASLR bypass. We do
this here rather than in kaddr_leak because perf_trace_event_perm() can
distinguish between kernel tracepoints and uprobe tracepoints, where the
IP is a safe user space address and is necessary for uprobe
functionality.
3. Restrict pure counting events (no PERF_SAMPLE_RAW) to
TRACE_EVENT_FL_CAP_ANY tracepoints preventing unprivileged users from
counting internal kernel tracepoints while preserving current
behavior for exclude_kernel=1 events.
Example usage after this change:
$ perf trace ls # works as unprivileged user
$ perf trace # system-wide, still requires privileges
$ perf trace -p 1234 # requires ptrace permission on pid 1234
Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <ashelat@redhat.com>
---
kernel/events/core.c | 28 +++++++++++++++++++++++++---
kernel/trace/trace_event_perf.c | 28 +++++++++++++++++++++++++++-
2 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 954c36e28101..48bfff07ae02 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -13910,9 +13910,31 @@ SYSCALL_DEFINE5(perf_event_open,
return err;
if (!attr.exclude_kernel) {
- err = perf_allow_kernel();
- if (err)
- return err;
+ bool tp_bypass = false;
+
+ /* Check unprivileged tracepoints */
+ if (attr.type == PERF_TYPE_TRACEPOINT && pid != -1) {
+ /*
+ * Block sample types that expose kernel addresses to
+ * prevent KASLR bypass
+ */
+ u64 kaddr_leak = PERF_SAMPLE_CALLCHAIN |
+ PERF_SAMPLE_BRANCH_STACK |
+ PERF_SAMPLE_ADDR |
+ PERF_SAMPLE_REGS_INTR;
+
+ tp_bypass = !(attr.sample_type & kaddr_leak);
+ }
+
+ if (!tp_bypass) {
+ err = perf_allow_kernel();
+ if (err)
+ return err;
+ } else {
+ err = security_perf_event_open(PERF_SECURITY_KERNEL);
+ if (err)
+ return err;
+ }
}
if (attr.namespaces) {
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 5b272856e5ab..a264154b460e 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -24,6 +24,16 @@ typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)])
/* Count the events in use (per event id, not per instance) */
static int total_ref_count;
+/* Check if perf tracepoint is restricted for unprivileged users */
+static bool perf_tp_is_restricted(struct perf_event *p_event)
+{
+ if (p_event->attr.exclude_kernel)
+ return false;
+ if (sysctl_perf_event_paranoid <= 1 || perfmon_capable())
+ return false;
+ return true;
+}
+
static int perf_trace_event_perm(struct trace_event_call *tp_event,
struct perf_event *p_event)
{
@@ -72,9 +82,25 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event,
return -EINVAL;
}
+ /*
+ * PERF_SAMPLE_IP on kernel tracepoints exposes a kernel text
+ * address, weakening KASLR. Block for unprivileged users unless
+ * the tracepoint is a uprobe (userspace IP, safe to expose).
+ */
+ if ((p_event->attr.sample_type & PERF_SAMPLE_IP) &&
+ !(tp_event->flags & TRACE_EVENT_FL_UPROBE) &&
+ perf_tp_is_restricted(p_event))
+ return -EACCES;
+
/* No tracing, just counting, so no obvious leak */
- if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
+ if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) {
+ /* Prevent unprivileged users from counting kernel tracepoints */
+ if (perf_tp_is_restricted(p_event) &&
+ !(p_event->attach_state == PERF_ATTACH_TASK &&
+ (tp_event->flags & TRACE_EVENT_FL_CAP_ANY)))
+ return -EACCES;
return 0;
+ }
/* Some events are ok to be traced by non-root users... */
if (p_event->attach_state == PERF_ATTACH_TASK) {
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-15 13:53 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 13:52 [PATCH v5 0/5] Enable perf tracing for unprivileged users Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 1/5] eventfs: define event fields before directory creation Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 2/5] tracefs: add read-only eventfs filesystem at /sys/kernel/events Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 3/5] perf tools: fall back to eventfs for unprivileged event discovery Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 4/5] perf evsel: don't set PERF_SAMPLE_IP for unprivileged tracepoints Anubhav Shelat
2026-07-15 13:52 ` [PATCH v5 5/5] perf: enable unprivileged syscall tracing with perf trace Anubhav Shelat
[not found] <20260714183150.292861-2-ashelat@redhat.com>
2026-07-14 18:31 ` Anubhav Shelat
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox