On Mon, Feb 19, 2024, at 19:35, Mickaël Salaün wrote: > vfs_masks_device_ioctl() and vfs_masks_device_ioctl_compat() are useful > to differenciate between device driver IOCTL implementations and > filesystem ones. The goal is to be able to filter well-defined IOCTLs > from per-device (i.e. namespaced) IOCTLs and control such access. > > Add a new ioctl_compat() helper, similar to vfs_ioctl(), to wrap > compat_ioctl() calls and handle error conversions. I'm still a bit confused by what your goal is here. I see the code getting more complex but don't see the payoff in this patch. > Cc: Arnd Bergmann <arnd@xxxxxxxx> > Cc: Christian Brauner <brauner@xxxxxxxxxx> > Cc: Günther Noack <gnoack@xxxxxxxxxx> I assume the missing Signed-off-by is intentional while you are still gathering feedback? > +/* > + * Safeguard to maintain a list of valid IOCTLs handled by > do_vfs_ioctl() > + * instead of def_blk_fops or def_chr_fops (see init_special_inode). > + */ > +__attribute_const__ bool vfs_masked_device_ioctl(const unsigned int > cmd) > +{ > + switch (cmd) { > + case FIOCLEX: > + case FIONCLEX: > + case FIONBIO: > + case FIOASYNC: > + case FIOQSIZE: > + case FIFREEZE: > + case FITHAW: > + case FS_IOC_FIEMAP: > + case FIGETBSZ: > + case FICLONE: > + case FICLONERANGE: > + case FIDEDUPERANGE: > + /* FIONREAD is forwarded to device implementations. */ > + case FS_IOC_GETFLAGS: > + case FS_IOC_SETFLAGS: > + case FS_IOC_FSGETXATTR: > + case FS_IOC_FSSETXATTR: > + /* file_ioctl()'s IOCTLs are forwarded to device implementations. */ > + return true; > + default: > + return false; > + } > +} > +EXPORT_SYMBOL(vfs_masked_device_ioctl); It looks like this gets added into the hot path of every ioctl command, which is not ideal, especially when this no longer gets inlined into the caller. > + inode = file_inode(f.file); > + is_device = S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode); > + if (is_device && !vfs_masked_device_ioctl(cmd)) { > + error = vfs_ioctl(f.file, cmd, arg); > + goto out; > + } The S_ISBLK() || S_ISCHR() check here looks like it changes behavior, at least for sockets. If that is intentional, it should probably be a separate patch with a detailed explanation. > error = do_vfs_ioctl(f.file, fd, cmd, arg); > - if (error == -ENOIOCTLCMD) > + if (error == -ENOIOCTLCMD) { > + WARN_ON_ONCE(is_device); > error = vfs_ioctl(f.file, cmd, arg); > + } The WARN_ON_ONCE() looks like it can be triggered from userspace, which is generally a bad idea. > +extern __attribute_const__ bool vfs_masked_device_ioctl(const unsigned > int cmd); > +#ifdef CONFIG_COMPAT > +extern __attribute_const__ bool > +vfs_masked_device_ioctl_compat(const unsigned int cmd); > +#else /* CONFIG_COMPAT */ > +static inline __attribute_const__ bool > +vfs_masked_device_ioctl_compat(const unsigned int cmd) > +{ > + return vfs_masked_device_ioctl(cmd); > +} > +#endif /* CONFIG_COMPAT */ I don't understand the purpose of the #else path here, this should not be needed. Arnd