Re: [PATCH v6 07/12] vfs: add O_CREAT|O_DIRECTORY to open*(2)
From: Jori Koolstra
Date: Fri Sep 25 2026 - 19:14:17 EST
> Op 18-09-2026 04:17 EDT schreef Christian Brauner <brauner@xxxxxxxxxx>:
>
>
> > @@ -4582,12 +4646,25 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
> > goto out_dput;
> > }
> >
> > - if (!dir_inode->i_op->create) {
> > + /* mimic operation missing errnos of vfs_mkdir/vfs_create */
> > + if (create_dir && !dir_inode->i_op->mkdir) {
> > + error = -EPERM;
> > + goto out_dput;
> > + }
> > + if (!create_dir && !dir_inode->i_op->create) {
> > error = -EACCES;
> > goto out_dput;
> > }
> >
> > - error = vfs_create_no_perm(idmap, dentry, mode, &delegated_inode);
> > + if (create_dir) {
> > + struct dentry *res = vfs_mkdir_no_perm(idmap, dir_inode, dentry, mode,
> > + &delegated_inode);
>
> So, I think this is broken. Whatever vfs_mkdir_no_perm() returns is
> passed to do_open(). Kernfs makes that buggy.
>
> cgroup, cgroup2, and resctrl are all implemented on top of kernfs. And
> kernfs ->mkdir:: iop never instantiates the dentry.
>
> So that means e.g.,
>
> openat(cgroup_dir, "subdir", O_CREAT|O_DIRECTORY) creates a cgroup
> and then fails with ENOTDIR.
>
> So the negative dentry gets handed out and now userspace holds an fd
> with that negative dentry. So say userspace does fchown() to 1000 and
> then fchmod() with the sticky bit and then you get a NULL deref. I have
> reproduced this.
>
Damn, nice catch. None of the LLMs caught this, only our flesh and blood
maintainer ;) I must admit I didn't consider a ->mkdir that left the dentry
negative.
I haven't looked into kernfs yet, but does it make sense you can set the
sticky bit there? Not that it is a viable solution to just mask that out,
as anyone would expect positive dentry at the time of do_open(). Curiously,
with a very quick glance, it does seem that only may_create_in_sticky() relies
on the inode being available.
Thanks for looking into this, Christian. See you at Plumbers, I guess?
Best wishes,
Jori.