mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD
Currently, SCM_PIDFD carries a pidfd for the thread-group leader. A broker or the coredump server cannot learn the identity of the specific thread that sent a given message. Now that both struct pids are recorded a receiver can ask for the specific identity it needs. So add SO_PASSPIDFD_THREAD as a sibling of SO_PASSPIDFD. SO_PASSPIDFD_THREAD delivers an SCM_PIDFD_THREAD, i.e. scoped to the specific thread sending. This is completely independent of SO_PASSPIDFD, meaning you can use both options and get both cmsgs. On SOCK_STREAM sockets recvmsg() only stops merging data at a thread boundary when the receiver asked for a thread pidfd. For SO_PASSCRED and SO_PASSPIDFD receivers all threads of one process remain a single writer. Co-developed-by: Andrew Halaney <andrew@amutable.com> Signed-off-by: Andrew Halaney <andrew@amutable.com> Link: https://patch.msgid.link/20261005-work-unix-passpidfd-v4-3-350183b6e02a@amutable.com Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
c48ae5bcfc
commit
be3a5e06b9
|
|
@ -157,6 +157,8 @@
|
||||||
|
|
||||||
#define SO_RIGHTS_NOTRUNC 85
|
#define SO_RIGHTS_NOTRUNC 85
|
||||||
|
|
||||||
|
#define SO_PASSPIDFD_THREAD 86
|
||||||
|
|
||||||
#if !defined(__KERNEL__)
|
#if !defined(__KERNEL__)
|
||||||
|
|
||||||
#if __BITS_PER_LONG == 64
|
#if __BITS_PER_LONG == 64
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,8 @@
|
||||||
|
|
||||||
#define SO_RIGHTS_NOTRUNC 85
|
#define SO_RIGHTS_NOTRUNC 85
|
||||||
|
|
||||||
|
#define SO_PASSPIDFD_THREAD 86
|
||||||
|
|
||||||
#if !defined(__KERNEL__)
|
#if !defined(__KERNEL__)
|
||||||
|
|
||||||
#if __BITS_PER_LONG == 64
|
#if __BITS_PER_LONG == 64
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,8 @@
|
||||||
|
|
||||||
#define SO_RIGHTS_NOTRUNC 0x4053
|
#define SO_RIGHTS_NOTRUNC 0x4053
|
||||||
|
|
||||||
|
#define SO_PASSPIDFD_THREAD 0x4054
|
||||||
|
|
||||||
#if !defined(__KERNEL__)
|
#if !defined(__KERNEL__)
|
||||||
|
|
||||||
#if __BITS_PER_LONG == 64
|
#if __BITS_PER_LONG == 64
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,8 @@
|
||||||
|
|
||||||
#define SO_RIGHTS_NOTRUNC 0x005e
|
#define SO_RIGHTS_NOTRUNC 0x005e
|
||||||
|
|
||||||
|
#define SO_PASSPIDFD_THREAD 0x005f
|
||||||
|
|
||||||
#if !defined(__KERNEL__)
|
#if !defined(__KERNEL__)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,7 @@ static inline size_t msg_data_left(const struct msghdr *msg)
|
||||||
#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */
|
#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */
|
||||||
#define SCM_SECURITY 0x03 /* rw: security label */
|
#define SCM_SECURITY 0x03 /* rw: security label */
|
||||||
#define SCM_PIDFD 0x04 /* ro: pidfd (int) */
|
#define SCM_PIDFD 0x04 /* ro: pidfd (int) */
|
||||||
|
#define SCM_PIDFD_THREAD 0x05 /* ro: thread pidfd (int) */
|
||||||
|
|
||||||
struct ucred {
|
struct ucred {
|
||||||
__u32 pid;
|
__u32 pid;
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ struct sk_filter;
|
||||||
* @sk_scm_security: flagged by SO_PASSSEC to recv SCM_SECURITY
|
* @sk_scm_security: flagged by SO_PASSSEC to recv SCM_SECURITY
|
||||||
* @sk_scm_pidfd: flagged by SO_PASSPIDFD to recv SCM_PIDFD
|
* @sk_scm_pidfd: flagged by SO_PASSPIDFD to recv SCM_PIDFD
|
||||||
* @sk_scm_rights: flagged by SO_PASSRIGHTS to recv SCM_RIGHTS
|
* @sk_scm_rights: flagged by SO_PASSRIGHTS to recv SCM_RIGHTS
|
||||||
|
* @sk_scm_pidfd_thread: flagged by SO_PASSPIDFD_THREAD to recv a thread SCM_PIDFD
|
||||||
* @sk_scm_unused: unused flags for scm_recv()
|
* @sk_scm_unused: unused flags for scm_recv()
|
||||||
* @ns_tracker: tracker for netns reference
|
* @ns_tracker: tracker for netns reference
|
||||||
* @sk_user_frags: xarray of pages the user is holding a reference on.
|
* @sk_user_frags: xarray of pages the user is holding a reference on.
|
||||||
|
|
@ -562,7 +563,8 @@ struct sock {
|
||||||
sk_scm_security : 1,
|
sk_scm_security : 1,
|
||||||
sk_scm_pidfd : 1,
|
sk_scm_pidfd : 1,
|
||||||
sk_scm_rights : 1,
|
sk_scm_rights : 1,
|
||||||
sk_scm_unused : 4;
|
sk_scm_pidfd_thread : 1,
|
||||||
|
sk_scm_unused : 3;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
u8 sk_clockid;
|
u8 sk_clockid;
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,8 @@
|
||||||
|
|
||||||
#define SO_RIGHTS_NOTRUNC 85
|
#define SO_RIGHTS_NOTRUNC 85
|
||||||
|
|
||||||
|
#define SO_PASSPIDFD_THREAD 86
|
||||||
|
|
||||||
#if !defined(__KERNEL__)
|
#if !defined(__KERNEL__)
|
||||||
|
|
||||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
|
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
|
||||||
|
|
|
||||||
|
|
@ -499,11 +499,16 @@ static bool scm_has_secdata(struct sock *sk)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
|
static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm,
|
||||||
|
enum pid_type type, int cmsg_type)
|
||||||
{
|
{
|
||||||
|
unsigned int flags = PIDFD_STALE;
|
||||||
struct file *pidfd_file = NULL;
|
struct file *pidfd_file = NULL;
|
||||||
|
struct pid *pid;
|
||||||
int len, pidfd;
|
int len, pidfd;
|
||||||
|
|
||||||
|
pid = scm->pid[type];
|
||||||
|
|
||||||
/* put_cmsg() doesn't return an error if CMSG is truncated,
|
/* put_cmsg() doesn't return an error if CMSG is truncated,
|
||||||
* that's why we need to opencode these checks here.
|
* that's why we need to opencode these checks here.
|
||||||
*/
|
*/
|
||||||
|
|
@ -517,12 +522,15 @@ static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!scm->pid[PIDTYPE_TGID])
|
if (!pid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pidfd = pidfd_prepare(scm->pid[PIDTYPE_TGID], PIDFD_STALE, &pidfd_file);
|
if (type == PIDTYPE_PID)
|
||||||
|
flags |= PIDFD_THREAD;
|
||||||
|
|
||||||
if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
|
pidfd = pidfd_prepare(pid, flags, &pidfd_file);
|
||||||
|
|
||||||
|
if (put_cmsg(msg, SOL_SOCKET, cmsg_type, sizeof(int), &pidfd)) {
|
||||||
if (pidfd_file) {
|
if (pidfd_file) {
|
||||||
put_unused_fd(pidfd);
|
put_unused_fd(pidfd);
|
||||||
fput(pidfd_file);
|
fput(pidfd_file);
|
||||||
|
|
@ -540,7 +548,7 @@ static bool __scm_recv_common(struct sock *sk, struct msghdr *msg,
|
||||||
{
|
{
|
||||||
if (!msg->msg_control) {
|
if (!msg->msg_control) {
|
||||||
if (sk->sk_scm_credentials || sk->sk_scm_pidfd ||
|
if (sk->sk_scm_credentials || sk->sk_scm_pidfd ||
|
||||||
scm->fp || scm_has_secdata(sk))
|
sk->sk_scm_pidfd_thread || scm->fp || scm_has_secdata(sk))
|
||||||
msg->msg_flags |= MSG_CTRUNC;
|
msg->msg_flags |= MSG_CTRUNC;
|
||||||
|
|
||||||
scm_destroy(scm);
|
scm_destroy(scm);
|
||||||
|
|
@ -587,7 +595,10 @@ void scm_recv_unix(struct socket *sock, struct msghdr *msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sock->sk->sk_scm_pidfd)
|
if (sock->sk->sk_scm_pidfd)
|
||||||
scm_pidfd_recv(msg, scm);
|
scm_pidfd_recv(msg, scm, PIDTYPE_TGID, SCM_PIDFD);
|
||||||
|
|
||||||
|
if (sock->sk->sk_scm_pidfd_thread)
|
||||||
|
scm_pidfd_recv(msg, scm, PIDTYPE_PID, SCM_PIDFD_THREAD);
|
||||||
|
|
||||||
scm_destroy_cred(scm);
|
scm_destroy_cred(scm);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1578,6 +1578,13 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
|
||||||
ret = -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SO_PASSPIDFD_THREAD:
|
||||||
|
if (sk_is_unix(sk))
|
||||||
|
sk->sk_scm_pidfd_thread = valbool;
|
||||||
|
else
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
|
||||||
case SO_PASSRIGHTS:
|
case SO_PASSRIGHTS:
|
||||||
if (sk_is_unix(sk))
|
if (sk_is_unix(sk))
|
||||||
sk->sk_scm_rights = valbool;
|
sk->sk_scm_rights = valbool;
|
||||||
|
|
@ -1893,6 +1900,13 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
|
||||||
v.val = sk->sk_scm_pidfd;
|
v.val = sk->sk_scm_pidfd;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SO_PASSPIDFD_THREAD:
|
||||||
|
if (!sk_is_unix(sk))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
v.val = sk->sk_scm_pidfd_thread;
|
||||||
|
break;
|
||||||
|
|
||||||
case SO_PASSRIGHTS:
|
case SO_PASSRIGHTS:
|
||||||
if (!sk_is_unix(sk))
|
if (!sk_is_unix(sk))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
|
||||||
|
|
@ -803,7 +803,8 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
|
||||||
|
|
||||||
static bool unix_may_passcred(const struct sock *sk)
|
static bool unix_may_passcred(const struct sock *sk)
|
||||||
{
|
{
|
||||||
return sk->sk_scm_credentials || sk->sk_scm_pidfd;
|
return sk->sk_scm_credentials || sk->sk_scm_pidfd ||
|
||||||
|
sk->sk_scm_pidfd_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unix_listen(struct socket *sock, int backlog)
|
static int unix_listen(struct socket *sock, int backlog)
|
||||||
|
|
@ -2046,9 +2047,14 @@ static int unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool unix_skb_scm_eq(struct sk_buff *skb,
|
static bool unix_skb_scm_eq(const struct sock *sk, struct sk_buff *skb,
|
||||||
struct scm_cookie *scm)
|
struct scm_cookie *scm)
|
||||||
{
|
{
|
||||||
|
/* Only a thread pidfd receiver can tell threads of one process apart. */
|
||||||
|
if (sk->sk_scm_pidfd_thread &&
|
||||||
|
UNIXCB(skb).pid[PIDTYPE_PID] != scm->pid[PIDTYPE_PID])
|
||||||
|
return false;
|
||||||
|
|
||||||
return UNIXCB(skb).pid[PIDTYPE_TGID] == scm->pid[PIDTYPE_TGID] &&
|
return UNIXCB(skb).pid[PIDTYPE_TGID] == scm->pid[PIDTYPE_TGID] &&
|
||||||
uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
|
uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
|
||||||
gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
|
gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
|
||||||
|
|
@ -3031,7 +3037,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
||||||
|
|
||||||
if (check_creds) {
|
if (check_creds) {
|
||||||
/* Never glue messages from different writers */
|
/* Never glue messages from different writers */
|
||||||
if (!unix_skb_scm_eq(skb, &scm))
|
if (!unix_skb_scm_eq(sk, skb, &scm))
|
||||||
break;
|
break;
|
||||||
} else if (unix_may_passcred(sk)) {
|
} else if (unix_may_passcred(sk)) {
|
||||||
/* Copy credentials */
|
/* Copy credentials */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user