[PATCH net-next v3 2/5] vfs: add function receive_fd_filtered() that makes LSM filtering explicit
From: Jori Koolstra
Date: Mon Jun 29 2026 - 15:43:12 EST
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 <jkoolstra@xxxxxxxxx>
---
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