Thread (4 messages) flat view 4 messages, 3 authors, 3d ago

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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help