summaryrefslogtreecommitdiff
path: root/fs/pidfs.c
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2024-03-12 12:39:44 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2024-03-13 22:53:53 +0300
commit9d9539db8638cfe053fcd1f441746f0e2c8c2d32 (patch)
treeade3be60a23f710040dc8856d9d386676b701bd2 /fs/pidfs.c
parentce0c1c92656e3ea3840c4a5c748aa352285cae9c (diff)
downloadlinux-9d9539db8638cfe053fcd1f441746f0e2c8c2d32.tar.xz
pidfs: remove config option
As Linus suggested this enables pidfs unconditionally. A key property to retain is the ability to compare pidfds by inode number (cf. [1]). That's extremely helpful just as comparing namespace file descriptors by inode number is. They are used in a variety of scenarios where they need to be compared, e.g., when receiving a pidfd via SO_PEERPIDFD from a socket to trivially authenticate a the sender and various other use-cases. For 64bit systems this is pretty trivial to do. For 32bit it's slightly more annoying as we discussed but we simply add a dumb ida based allocator that gets used on 32bit. This gives the same guarantees about inode numbers on 64bit without any overflow risk. Practically, we'll never run into overflow issues because we're constrained by the number of processes that can exist on 32bit and by the number of open files that can exist on a 32bit system. On 64bit none of this matters and things are very simple. If 32bit also needs the uniqueness guarantee they can simply parse the contents of /proc/<pid>/fd/<nr>. The uniqueness guarantees have a variety of use-cases. One of the most obvious ones is that they will make pidfiles (or "pidfdfiles", I guess) reliable as the unique identifier can be placed into there that won't be reycled. Also a frequent request. Note, I took the chance and simplified path_from_stashed() even further. Instead of passing the inode number explicitly to path_from_stashed() we let the filesystem handle that internally. So path_from_stashed() ends up even simpler than it is now. This is also a good solution allowing the cleanup code to be clean and consistent between 32bit and 64bit. The cleanup path in prepare_anon_dentry() is also switched around so we put the inode before the dentry allocation. This means we only have to call the cleanup handler for the filesystem's inode data once and can rely ->evict_inode() otherwise. Aside from having to have a bit of extra code for 32bit it actually ends up a nice cleanup for path_from_stashed() imho. Tested on both 32 and 64bit including error injection. Link: https://github.com/systemd/systemd/pull/31713 [1] Link: https://lore.kernel.org/r/20240312-dingo-sehnlich-b3ecc35c6de7@brauner Signed-off-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/pidfs.c')
-rw-r--r--fs/pidfs.c101
1 files changed, 47 insertions, 54 deletions
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 8fd71a00be9c..a63d5d24aa02 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -16,17 +16,6 @@
#include "internal.h"
-static int pidfd_release(struct inode *inode, struct file *file)
-{
-#ifndef CONFIG_FS_PID
- struct pid *pid = file->private_data;
-
- file->private_data = NULL;
- put_pid(pid);
-#endif
- return 0;
-}
-
#ifdef CONFIG_PROC_FS
/**
* pidfd_show_fdinfo - print information about a pidfd
@@ -120,7 +109,6 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
}
static const struct file_operations pidfs_file_operations = {
- .release = pidfd_release,
.poll = pidfd_poll,
#ifdef CONFIG_PROC_FS
.show_fdinfo = pidfd_show_fdinfo,
@@ -131,16 +119,45 @@ struct pid *pidfd_pid(const struct file *file)
{
if (file->f_op != &pidfs_file_operations)
return ERR_PTR(-EBADF);
-#ifdef CONFIG_FS_PID
return file_inode(file)->i_private;
-#else
- return file->private_data;
-#endif
}
-#ifdef CONFIG_FS_PID
static struct vfsmount *pidfs_mnt __ro_after_init;
+#if BITS_PER_LONG == 32
+/*
+ * Provide a fallback mechanism for 32-bit systems so processes remain
+ * reliably comparable by inode number even on those systems.
+ */
+static DEFINE_IDA(pidfd_inum_ida);
+
+static int pidfs_inum(struct pid *pid, unsigned long *ino)
+{
+ int ret;
+
+ ret = ida_alloc_range(&pidfd_inum_ida, RESERVED_PIDS + 1,
+ UINT_MAX, GFP_ATOMIC);
+ if (ret < 0)
+ return -ENOSPC;
+
+ *ino = ret;
+ return 0;
+}
+
+static inline void pidfs_free_inum(unsigned long ino)
+{
+ if (ino > 0)
+ ida_free(&pidfd_inum_ida, ino);
+}
+#else
+static inline int pidfs_inum(struct pid *pid, unsigned long *ino)
+{
+ *ino = pid->ino;
+ return 0;
+}
+#define pidfs_free_inum(ino) ((void)(ino))
+#endif
+
/*
* The vfs falls back to simple_setattr() if i_op->setattr() isn't
* implemented. Let's reject it completely until we have a clean
@@ -173,6 +190,7 @@ static void pidfs_evict_inode(struct inode *inode)
clear_inode(inode);
put_pid(pid);
+ pidfs_free_inum(inode->i_ino);
}
static const struct super_operations pidfs_sops = {
@@ -183,8 +201,10 @@ static const struct super_operations pidfs_sops = {
static char *pidfs_dname(struct dentry *dentry, char *buffer, int buflen)
{
- return dynamic_dname(buffer, buflen, "pidfd:[%lu]",
- d_inode(dentry)->i_ino);
+ struct inode *inode = d_inode(dentry);
+ struct pid *pid = inode->i_private;
+
+ return dynamic_dname(buffer, buflen, "pidfd:[%llu]", pid->ino);
}
static const struct dentry_operations pidfs_dentry_operations = {
@@ -193,13 +213,19 @@ static const struct dentry_operations pidfs_dentry_operations = {
.d_prune = stashed_dentry_prune,
};
-static void pidfs_init_inode(struct inode *inode, void *data)
+static int pidfs_init_inode(struct inode *inode, void *data)
{
inode->i_private = data;
inode->i_flags |= S_PRIVATE;
inode->i_mode |= S_IRWXU;
inode->i_op = &pidfs_inode_operations;
inode->i_fop = &pidfs_file_operations;
+ /*
+ * Inode numbering for pidfs start at RESERVED_PIDS + 1. This
+ * avoids collisions with the root inode which is 1 for pseudo
+ * filesystems.
+ */
+ return pidfs_inum(data, &inode->i_ino);
}
static void pidfs_put_data(void *data)
@@ -240,13 +266,7 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags)
struct path path;
int ret;
- /*
- * Inode numbering for pidfs start at RESERVED_PIDS + 1.
- * This avoids collisions with the root inode which is 1
- * for pseudo filesystems.
- */
- ret = path_from_stashed(&pid->stashed, pid->ino, pidfs_mnt,
- get_pid(pid), &path);
+ ret = path_from_stashed(&pid->stashed, pidfs_mnt, get_pid(pid), &path);
if (ret < 0)
return ERR_PTR(ret);
@@ -261,30 +281,3 @@ void __init pidfs_init(void)
if (IS_ERR(pidfs_mnt))
panic("Failed to mount pidfs pseudo filesystem");
}
-
-bool is_pidfs_sb(const struct super_block *sb)
-{
- return sb == pidfs_mnt->mnt_sb;
-}
-
-#else /* !CONFIG_FS_PID */
-
-struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags)
-{
- struct file *pidfd_file;
-
- pidfd_file = anon_inode_getfile("[pidfd]", &pidfs_file_operations, pid,
- flags | O_RDWR);
- if (IS_ERR(pidfd_file))
- return pidfd_file;
-
- get_pid(pid);
- return pidfd_file;
-}
-
-void __init pidfs_init(void) { }
-bool is_pidfs_sb(const struct super_block *sb)
-{
- return false;
-}
-#endif