[PATCH RFC v3 08/10] net, pidfs, coredump: only allow coredumping tasks to connect to coredump socket

From: Christian Brauner
Date: Mon May 05 2025 - 07:17:03 EST


Make sure that only tasks that actually coredumped may connect to the
coredump socket. This restriction may be loosened later in case
userspace processes would like to use it to generate their own
coredumps. Though it'd be wiser if userspace just exposed a separate
socket for that.

Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>
---
fs/coredump.c | 25 +++++++++++++++++++++++++
fs/pidfs.c | 13 +++++++++++++
include/linux/coredump.h | 12 ++++++++++++
include/linux/pidfs.h | 1 +
net/unix/af_unix.c | 19 +++++++++++++------
5 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/fs/coredump.c b/fs/coredump.c
index a5f17aaeee32..8a9620ce4fd0 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -630,6 +630,31 @@ struct sockaddr_un coredump_unix_socket = {
#define COREDUMP_UNIX_SOCKET_ADDR_SIZE \
(offsetof(struct sockaddr_un, sun_path) + \
sizeof("\0linuxafsk/coredump.socket") - 1)
+
+static inline bool is_coredump_socket(const struct sockaddr_un *sunname,
+ unsigned int len)
+{
+ return (COREDUMP_UNIX_SOCKET_ADDR_SIZE == len) &&
+ !memcmp(&coredump_unix_socket, sunname, len);
+}
+
+/*
+ * For the coredump socket in the initial network namespace we only
+ * allow actual coredumping processes to connect to it, i.e., the kernel
+ * itself.
+ */
+bool unix_use_coredump_socket(const struct net *net, const struct pid *pid,
+ const struct sockaddr_un *sunname,
+ unsigned int len)
+{
+ if (net != &init_net)
+ return true;
+
+ if (!is_coredump_socket(sunname, len))
+ return true;
+
+ return pidfs_pid_coredumped(pid);
+}
#endif

void do_coredump(const kernel_siginfo_t *siginfo)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 8c4d83fb115b..e0a4c34ddc42 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -258,6 +258,19 @@ static __u32 pidfs_coredump_mask(unsigned long mm_flags)
return 0;
}

+bool pidfs_pid_coredumped(const struct pid *pid)
+{
+ struct inode *inode;
+
+ if (!pid)
+ return false;
+
+ /* We expect the caller to hold a reference to @pid->stashed. */
+ VFS_WARN_ON_ONCE(!pid->stashed);
+ inode = d_inode(pid->stashed);
+ return (READ_ONCE(pidfs_i(inode)->__pei.coredump_mask) & PIDFD_COREDUMPED);
+}
+
static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
{
struct pidfd_info __user *uinfo = (struct pidfd_info __user *)arg;
diff --git a/include/linux/coredump.h b/include/linux/coredump.h
index 76e41805b92d..2b2f0c016c16 100644
--- a/include/linux/coredump.h
+++ b/include/linux/coredump.h
@@ -7,6 +7,8 @@
#include <linux/fs.h>
#include <asm/siginfo.h>

+struct sockaddr_un;
+
#ifdef CONFIG_COREDUMP
struct core_vma_metadata {
unsigned long start, end;
@@ -44,6 +46,9 @@ extern int dump_align(struct coredump_params *cprm, int align);
int dump_user_range(struct coredump_params *cprm, unsigned long start,
unsigned long len);
extern void do_coredump(const kernel_siginfo_t *siginfo);
+bool unix_use_coredump_socket(const struct net *net, const struct pid *pid,
+ const struct sockaddr_un *sunname,
+ unsigned int len);

/*
* Logging for the coredump code, ratelimited.
@@ -64,6 +69,13 @@ extern void do_coredump(const kernel_siginfo_t *siginfo);

#else
static inline void do_coredump(const kernel_siginfo_t *siginfo) {}
+static inline bool unix_use_coredump_socket(const struct net *net,
+ const struct pid *pid,
+ const struct sockaddr_un *sunname,
+ unsigned int len)
+{
+ return true;
+}

#define coredump_report(...)
#define coredump_report_failure(...)
diff --git a/include/linux/pidfs.h b/include/linux/pidfs.h
index f7729b9371bc..f9c287c0e0ff 100644
--- a/include/linux/pidfs.h
+++ b/include/linux/pidfs.h
@@ -14,5 +14,6 @@ extern const struct dentry_operations pidfs_dentry_operations;
int pidfs_register_pid(struct pid *pid);
void pidfs_get_pid(struct pid *pid);
void pidfs_put_pid(struct pid *pid);
+bool pidfs_pid_coredumped(const struct pid *pid);

#endif /* _LINUX_PID_FS_H */
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index edc2f143f401..613cf9225381 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -101,6 +101,7 @@
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/pidfs.h>
+#include <linux/coredump.h>
#include <net/af_unix.h>
#include <net/net_namespace.h>
#include <net/scm.h>
@@ -1217,6 +1218,7 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
}

static struct sock *unix_find_abstract(struct net *net,
+ const struct pid *peer_pid,
struct sockaddr_un *sunaddr,
int addr_len, int type)
{
@@ -1224,6 +1226,9 @@ static struct sock *unix_find_abstract(struct net *net,
struct dentry *dentry;
struct sock *sk;

+ if (!unix_use_coredump_socket(net, peer_pid, sunaddr, addr_len))
+ return ERR_PTR(-ECONNREFUSED);
+
sk = unix_find_socket_byname(net, sunaddr, addr_len, hash);
if (!sk)
return ERR_PTR(-ECONNREFUSED);
@@ -1236,15 +1241,16 @@ static struct sock *unix_find_abstract(struct net *net,
}

static struct sock *unix_find_other(struct net *net,
- struct sockaddr_un *sunaddr,
- int addr_len, int type)
+ const struct pid *peer_pid,
+ struct sockaddr_un *sunaddr, int addr_len,
+ int type)
{
struct sock *sk;

if (sunaddr->sun_path[0])
sk = unix_find_bsd(sunaddr, addr_len, type);
else
- sk = unix_find_abstract(net, sunaddr, addr_len, type);
+ sk = unix_find_abstract(net, peer_pid, sunaddr, addr_len, type);

return sk;
}
@@ -1500,7 +1506,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
}

restart:
- other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type);
+ other = unix_find_other(sock_net(sk), NULL, sunaddr, alen, sock->type);
if (IS_ERR(other)) {
err = PTR_ERR(other);
goto out;
@@ -1647,7 +1653,8 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,

restart:
/* Find listening sock. */
- other = unix_find_other(net, sunaddr, addr_len, sk->sk_type);
+ other = unix_find_other(net, peercred.peer_pid, sunaddr, addr_len,
+ sk->sk_type);
if (IS_ERR(other)) {
err = PTR_ERR(other);
goto out_free_skb;
@@ -2115,7 +2122,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,

if (msg->msg_namelen) {
lookup:
- other = unix_find_other(sock_net(sk), msg->msg_name,
+ other = unix_find_other(sock_net(sk), NULL, msg->msg_name,
msg->msg_namelen, sk->sk_type);
if (IS_ERR(other)) {
err = PTR_ERR(other);

--
2.47.2