Re: [PATCH v3] tracefs: Add read-only eventfs filesystem at /sys/kernel/events
From: Steven Rostedt <rostedt@goodmis.org>
Date: 2026-08-10 21:39:17
Also in:
lkml, sashiko-reviews
Replying to Sashiko: https://sashiko.dev/#/patchset/20260810160708.3460a2fd%40gandalf.local.home
quoted hunk ↗ jump to hunk
--- Patch [1]: [PATCH v3] tracefs: Add read-only eventfs filesystem at /sys/kernel/events ---commit 5fedbb447b6c46a029edb4492a02145a903d2475 Author: Anubhav Shelat [off-list ref] tracefs: Add read-only eventfs filesystem at /sys/kernel/events This commit introduces a read-only pseudo-filesystem "eventfs" mounted at /sys/kernel/events, exposing trace event format and id files to unprivileged users. It reuses the eventfs_inode lazy-lookup infrastructure with new super_operations to share the tracefs inode allocator.quoted
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index a524584353279..3af1cf5abe4ab 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c[ ... ]quoted
@@ -561,20 +653,40 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,[ ... ]quoted
/* * Walk the children of a eventfs_inode to fill in getdents(). */ -static int eventfs_iterate(struct file *file, struct dir_context *ctx) +static int __eventfs_iterate(struct file *file, struct dir_context *ctx, bool ro) { const struct file_operations *fops; struct inode *f_inode = file_inode(file);This is a pre-existing issue, but does exposing this directory iteration code to unprivileged users create a severe security vulnerability? Looking at __eventfs_iterate(), there is an existing integer truncation when setting up the loop: fs/tracefs/event_inode.c:__eventfs_iterate() { ... c = ctx->pos - 2; ... for (i = c; i < ei->nr_entries; i++, ctx->pos++) { ... entry = &ei->entries[i]; ... r = entry->callback(name, &mode, &cdata, &fops); If an attacker uses lseek() to set ctx->pos to 0x100000000, ctx->pos - 2 truncates into the 32-bit signed integer 'c', resulting in c = -2.
Once again, this is not possible. That's because both eventfs and tracefs have their super_block maxbytes set to the default which is 0x7fffffff. Anything more than that will return an error. You can't use lseek() bigger than that.
Since the 30-bit unsigned bitfield ei->nr_entries is promoted to a signed int in C, the loop condition (i < ei->nr_entries) evaluates as (-2 < 5), which is true. This allows a negative index to be used in &ei->entries[i], reading out-of-bounds memory to find the function pointer entry->callback, which is then immediately executed. Because this patch makes eventfs available to unprivileged users via the new 0555 mount, can this be used by any local user to trigger the out-of-bounds read and control-flow hijack?
With the following code:
readdir.c:
-------------------------------8<-------------------------------
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
unsigned long long addr;
struct dirent *dirent;
DIR *dir;
char *file;
int fd;
if (argc != 3) {
fprintf(stderr,"usage: %s dir addr\n",argv[0]);
exit(-1);
}
file = argv[1];
addr = strtoull(argv[2], NULL, 0);
fd = open(file, O_RDONLY);
if (fd < 0) {
perror("open");
exit(0);
}
printf("lseek to %llx\n", addr);
if (lseek64(fd, addr, SEEK_SET) == (off_t)-1) {
perror("lseek");
exit(-1);
}
dir = fdopendir(fd);
dirent = readdir(dir);
if (!dirent) {
printf("Nothing to read\n");
exit(0);
}
printf("entry: %s\n", dirent->d_name);
return 0;
}
------------------------------->8-------------------------------
I run:
~# ./readdir /sys/kernel/events 0x7fffffff
lseek to 7fffffff
Nothing to read
~# ./readdir /sys/kernel/events/sched 0x7fffffff
lseek to 7fffffff
Nothing to read
~# ./readdir /sys/kernel/events/sched/sched_switch 0x7fffffff
lseek to 7fffffff
Nothing to read
~# ./readdir /sys/kernel/events/ 0x100000000
lseek to 100000000
lseek: Invalid argument
~# ./readdir /sys/kernel/events/sched 0x100000000
lseek to 100000000
lseek: Invalid argument
~# ./readdir /sys/kernel/events/sched/sched_switch 0x100000000
lseek to 100000000
lseek: Invalid argument
~# ./readdir /sys/kernel/events/ 2
lseek to 2
entry: header_page
~# ./readdir /sys/kernel/events/sched 2
lseek to 2
entry: sched_wake_idle_without_ipi
~# ./readdir /sys/kernel/events/sched/sched_switch/ 2
lseek to 2
entry: format
How can we hit the condition that Sashiko is reporting?
-- Steve