Replying to Sashiko: 
https://sashiko.dev/#/patchset/20260810160708.3460a2fd%40gandalf.local.home

> --- Patch [1]: [PATCH v3] tracefs: Add read-only eventfs filesystem at 
> /sys/kernel/events ---
> commit 5fedbb447b6c46a029edb4492a02145a903d2475
> Author: Anubhav Shelat <[email protected]>
> 
> 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.
> 
> > 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
> [ ... ]
> > @@ -561,20 +653,40 @@ static struct dentry *eventfs_root_lookup(struct 
> > inode *dir,
> [ ... ]
> >  /*
> >   * 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

Reply via email to