On Wed, Jun 17, 2020 at 03:35:20PM +0000, David Laight wrote: > From: Kees Cook > > Sent: 16 June 2020 04:25 > > > > For both pidfd and seccomp, the __user pointer is not used. Update > > __fd_install_received() to make writing to ufd optional. (ufd > > itself cannot checked for NULL because this changes the SCM_RIGHTS > > interface behavior.) In these cases, the new fd needs to be returned > > on success. Update the existing callers to handle it. Add new wrapper > > fd_install_received() for pidfd and seccomp that does not use the ufd > > argument. > ...> > > static inline int fd_install_received_user(struct file *file, int __user *ufd, > > unsigned int o_flags) > > { > > - return __fd_install_received(file, ufd, o_flags); > > + return __fd_install_received(file, true, ufd, o_flags); > > +} > > Can you get rid of the 'return user' parameter by adding > if (!ufd) return -EFAULT; > to the above wrapper, then checking for NULL in the function? > > Or does that do the wrong horrid things in the fail path? Oh, hm. No, that shouldn't break the failure path, since everything gets unwound in __fd_install_received if the ufd write fails. Effectively this (I'll chop it up into the correct patches): diff --git a/fs/file.c b/fs/file.c index b583e7c60571..3b80324a31cc 100644 --- a/fs/file.c +++ b/fs/file.c @@ -939,18 +939,16 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags) * * @fd: fd to install into (if negative, a new fd will be allocated) * @file: struct file that was received from another process - * @ufd_required: true to use @ufd for writing fd number to userspace * @ufd: __user pointer to write new fd number to * @o_flags: the O_* flags to apply to the new fd entry * * Installs a received file into the file descriptor table, with appropriate * checks and count updates. Optionally writes the fd number to userspace, if - * @ufd_required is true (@ufd cannot just be tested for NULL because NULL may - * actually get passed into SCM_RIGHTS). + * @ufd is non-NULL. * * Returns newly install fd or -ve on error. */ -int __fd_install_received(int fd, struct file *file, bool ufd_required, +int __fd_install_received(int fd, struct file *file, int __user *ufd, unsigned int o_flags) { struct socket *sock; @@ -967,7 +965,7 @@ int __fd_install_received(int fd, struct file *file, bool ufd_required, return new_fd; } - if (ufd_required) { + if (ufd) { error = put_user(new_fd, ufd); if (error) { put_unused_fd(new_fd); diff --git a/include/linux/file.h b/include/linux/file.h index f1d16e24a12e..2ade0d90bc5e 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -91,20 +91,22 @@ extern void put_unused_fd(unsigned int fd); extern void fd_install(unsigned int fd, struct file *file); -extern int __fd_install_received(int fd, struct file *file, bool ufd_required, +extern int __fd_install_received(int fd, struct file *file, int __user *ufd, unsigned int o_flags); static inline int fd_install_received_user(struct file *file, int __user *ufd, unsigned int o_flags) { - return __fd_install_received(-1, file, true, ufd, o_flags); + if (ufd == NULL) + return -EFAULT; + return __fd_install_received(-1, file, ufd, o_flags); } static inline int fd_install_received(struct file *file, unsigned int o_flags) { - return __fd_install_received(-1, file, false, NULL, o_flags); + return __fd_install_received(-1, file, NULL, o_flags); } static inline int fd_replace_received(int fd, struct file *file, unsigned int o_flags) { - return __fd_install_received(fd, file, false, NULL, o_flags); + return __fd_install_received(fd, file, NULL, o_flags); } extern void flush_delayed_fput(void); -- Kees Cook