On Fri, Jun 12, 2020 at 08:13:25AM -0700, Kees Cook wrote: > On Fri, Jun 12, 2020 at 10:46:30AM +0000, Sargun Dhillon wrote: > > My suggest, written out (no idea if this code actually works), is as follows: > > > > ioctl.h: > > /* This needs to be added */ > > #define IOCDIR_MASK (_IOC_DIRMASK << _IOC_DIRSHIFT) > > This exists already: > > #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) > > > > > > > seccomp.h: > > > > struct struct seccomp_notif_addfd { > > __u64 fd; > > ... > > } > > > > /* or IOW? */ > > #define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOWR(3, struct seccomp_notif_addfd) > > > > seccomp.c: > > static long seccomp_notify_addfd(struct seccomp_filter *filter, > > struct seccomp_notif_addfd __user *uaddfd int size) > > { > > struct seccomp_notif_addfd addfd; > > int ret; > > > > if (size < 32) > > return -EINVAL; > > if (size > PAGE_SIZE) > > return -E2BIG; > > (Tanget: what was the reason for copy_struct_from_user() not including > the min/max check? I have a memory of Al objecting to having an > "internal" limit?) Al didn't want the PAGE_SIZE limit in there because there's nothing inherently wrong with copying insane amounts of memory. (Another tangent. I've asked this on Twitter not too long ago: do we have stats how long copy_from_user()/copy_struct_from_user() takes with growing struct/memory size? I'd be really interested in this. I have a feeling that clone3()'s and - having had a chat with David Howells - openat2()'s structs will continue to grow for a while... and I'd really like to have some numbers on when copy_struct_from_user() becomes costly or how costly it becomes.) > > > > > ret = copy_struct_from_user(&addfd, sizeof(addfd), uaddfd, size); > > if (ret) > > return ret; > > > > ... > > } > > > > /* Mask out size */ > > #define SIZE_MASK(cmd) (~IOCSIZE_MASK & cmd) > > > > /* Mask out direction */ > > #define DIR_MASK(cmd) (~IOCDIR_MASK & cmd) > > > > static long seccomp_notify_ioctl(struct file *file, unsigned int cmd, > > unsigned long arg) > > { > > struct seccomp_filter *filter = file->private_data; > > void __user *buf = (void __user *)arg; > > > > /* Fixed size ioctls. Can be converted later on? */ > > switch (cmd) { > > case SECCOMP_IOCTL_NOTIF_RECV: > > return seccomp_notify_recv(filter, buf); > > case SECCOMP_IOCTL_NOTIF_SEND: > > return seccomp_notify_send(filter, buf); > > case SECCOMP_IOCTL_NOTIF_ID_VALID: > > return seccomp_notify_id_valid(filter, buf); > > } > > > > /* Probably should make some nicer macros here */ > > switch (SIZE_MASK(DIR_MASK(cmd))) { > > case SIZE_MASK(DIR_MASK(SECCOMP_IOCTL_NOTIF_ADDFD)): > > Ah yeah, I like this because of what you mention below: it's forward > compat too. (I'd just use the ioctl masks directly...) > > switch (cmd & ~(_IOC_SIZEMASK | _IOC_DIRMASK)) > > > return seccomp_notify_addfd(filter, buf, _IOC_SIZE(cmd)); > > I really like that this ends up having the same construction as a > standard EA syscall: the size is part of the syscall arguments. This is basically what I had proposed in my previous mail, right? Christian