[PATCH net-next v3 2/5] vfs: add function receive_fd_filtered() that makes LSM filtering explicit
From: Jori Koolstra <hidden>
Date: 2026-06-29 19:42:31
Also in:
linux-fsdevel, lkml
Subsystem:
filesystems (vfs and infrastructure), the rest · Maintainers:
Alexander Viro, Christian Brauner, Linus Torvalds
To prepare for filtering LSM blocked fds received with SCM_RIGHTS, we first need to know when a received fd was filtered. Currently, receive_fd() relays the error returned by security_file_receive(). As there is no strict convention about what errnos this LSM hook can return, the caller of receive_fd() has no robust way of knowing whether an error is returned because the LSM blocked the fd, or because of some other failure (put_user, FD_PREPARE, etc.) Fix this by adding receive_fd_filtered() which carries an out-argument that is set only on LSM error. Signed-off-by: Jori Koolstra <redacted> --- fs/file.c | 48 +++++++++++++++++++++++++++++--------------- include/linux/file.h | 2 ++ 2 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/fs/file.c b/fs/file.c
index 628ca07dc4b1..2bc22cc69e84 100644
--- a/fs/file.c
+++ b/fs/file.c@@ -1367,6 +1367,25 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags) return err; } +static int __receive_fd(struct file *file, int __user *ufd, unsigned int o_flags) +{ + int error; + + FD_PREPARE(fdf, o_flags, file); + if (fdf.err) + return fdf.err; + get_file(file); + + if (ufd) { + error = put_user(fd_prepare_fd(fdf), ufd); + if (error) + return error; + } + + __receive_sock(fd_prepare_file(fdf)); + return fd_publish(fdf); +} + /** * receive_fd() - Install received file into file descriptor table * @file: struct file that was received from another process
@@ -1384,27 +1403,24 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags) */ int receive_fd(struct file *file, int __user *ufd, unsigned int o_flags) { - int error; - - error = security_file_receive(file); + int error = security_file_receive(file); if (error) return error; + return __receive_fd(file, ufd, o_flags); +} +EXPORT_SYMBOL_GPL(receive_fd); - FD_PREPARE(fdf, o_flags, file); - if (fdf.err) - return fdf.err; - get_file(file); - - if (ufd) { - error = put_user(fd_prepare_fd(fdf), ufd); - if (error) - return error; +int receive_fd_filtered(struct file *file, int __user *ufd, unsigned int o_flags, + bool *filtered) +{ + int error = security_file_receive(file); + if (error) { + *filtered = true; + return error; } - - __receive_sock(fd_prepare_file(fdf)); - return fd_publish(fdf); + *filtered = false; + return __receive_fd(file, ufd, o_flags); } -EXPORT_SYMBOL_GPL(receive_fd); int receive_fd_replace(int new_fd, struct file *file, unsigned int o_flags) {
diff --git a/include/linux/file.h b/include/linux/file.h
index 27484b444d31..748f08470bb4 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h@@ -119,6 +119,8 @@ DEFINE_FREE(fput, struct file *, if (!IS_ERR_OR_NULL(_T)) fput(_T)) extern void fd_install(unsigned int fd, struct file *file); int receive_fd(struct file *file, int __user *ufd, unsigned int o_flags); +int receive_fd_filtered(struct file *file, int __user *ufd, unsigned int o_flags, + bool *filtered); int receive_fd_replace(int new_fd, struct file *file, unsigned int o_flags);
--
2.54.0