fs: Expand __receive_fd() to accept existing fd

Expand __receive_fd() with support for replace_fd() for the coming seccomp
"addfd" ioctl(). Add new wrapper receive_fd_replace() for the new behavior
and update existing wrappers to retain old behavior.

Thanks to Colin Ian King <colin.king@canonical.com> for pointing out an
uninitialized variable exposure in an earlier version of this patch.

Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Dmitry Kadashev <dkadashev@gmail.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-fsdevel@vger.kernel.org
Reviewed-by: Sargun Dhillon <sargun@sargun.me>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Kees Cook 2020-06-10 08:46:58 -07:00
parent 910d2f16ac
commit 173817151b
2 changed files with 26 additions and 9 deletions

View File

@ -939,6 +939,7 @@ out_unlock:
/** /**
* __receive_fd() - Install received file into file descriptor table * __receive_fd() - Install received file into file descriptor table
* *
* @fd: fd to install into (if negative, a new fd will be allocated)
* @file: struct file that was received from another process * @file: struct file that was received from another process
* @ufd: __user pointer to write new fd number to * @ufd: __user pointer to write new fd number to
* @o_flags: the O_* flags to apply to the new fd entry * @o_flags: the O_* flags to apply to the new fd entry
@ -952,7 +953,7 @@ out_unlock:
* *
* Returns newly install fd or -ve on error. * Returns newly install fd or -ve on error.
*/ */
int __receive_fd(struct file *file, int __user *ufd, unsigned int o_flags) int __receive_fd(int fd, struct file *file, int __user *ufd, unsigned int o_flags)
{ {
int new_fd; int new_fd;
int error; int error;
@ -961,21 +962,33 @@ int __receive_fd(struct file *file, int __user *ufd, unsigned int o_flags)
if (error) if (error)
return error; return error;
if (fd < 0) {
new_fd = get_unused_fd_flags(o_flags); new_fd = get_unused_fd_flags(o_flags);
if (new_fd < 0) if (new_fd < 0)
return new_fd; return new_fd;
} else {
new_fd = fd;
}
if (ufd) { if (ufd) {
error = put_user(new_fd, ufd); error = put_user(new_fd, ufd);
if (error) { if (error) {
if (fd < 0)
put_unused_fd(new_fd); put_unused_fd(new_fd);
return error; return error;
} }
} }
if (fd < 0) {
fd_install(new_fd, get_file(file));
} else {
error = replace_fd(new_fd, file, o_flags);
if (error)
return error;
}
/* Bump the sock usage counts, if any. */ /* Bump the sock usage counts, if any. */
__receive_sock(file); __receive_sock(file);
fd_install(new_fd, get_file(file));
return new_fd; return new_fd;
} }

View File

@ -92,18 +92,22 @@ extern void put_unused_fd(unsigned int fd);
extern void fd_install(unsigned int fd, struct file *file); extern void fd_install(unsigned int fd, struct file *file);
extern int __receive_fd(struct file *file, int __user *ufd, extern int __receive_fd(int fd, struct file *file, int __user *ufd,
unsigned int o_flags); unsigned int o_flags);
static inline int receive_fd_user(struct file *file, int __user *ufd, static inline int receive_fd_user(struct file *file, int __user *ufd,
unsigned int o_flags) unsigned int o_flags)
{ {
if (ufd == NULL) if (ufd == NULL)
return -EFAULT; return -EFAULT;
return __receive_fd(file, ufd, o_flags); return __receive_fd(-1, file, ufd, o_flags);
} }
static inline int receive_fd(struct file *file, unsigned int o_flags) static inline int receive_fd(struct file *file, unsigned int o_flags)
{ {
return __receive_fd(file, NULL, o_flags); return __receive_fd(-1, file, NULL, o_flags);
}
static inline int receive_fd_replace(int fd, struct file *file, unsigned int o_flags)
{
return __receive_fd(fd, file, NULL, o_flags);
} }
extern void flush_delayed_fput(void); extern void flush_delayed_fput(void);