Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scm: add SO_PASSPIDFD and SCM_PIDFD

Implement SCM_PIDFD, a new type of CMSG type analogical to SCM_CREDENTIALS,
but it contains pidfd instead of plain pid, which allows programmers not
to care about PID reuse problem.

We mask SO_PASSPIDFD feature if CONFIG_UNIX is not builtin because
it depends on a pidfd_prepare() API which is not exported to the kernel
modules.

Idea comes from UAPI kernel group:
https://uapi-group.org/kernel-features/

Big thanks to Christian Brauner and Lennart Poettering for productive
discussions about this.

Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: David Ahern <dsahern@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Kees Cook <keescook@chromium.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Kuniyuki Iwashima <kuniyu@amazon.com>
Cc: Lennart Poettering <mzxreary@0pointer.de>
Cc: Luca Boccassi <bluca@debian.org>
Cc: linux-kernel@vger.kernel.org
Cc: netdev@vger.kernel.org
Cc: linux-arch@vger.kernel.org
Tested-by: Luca Boccassi <bluca@debian.org>
Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Alexander Mikhalitsyn and committed by
David S. Miller
5e2ff670 55d7c914

+76 -7
+2
arch/alpha/include/uapi/asm/socket.h
··· 137 137 138 138 #define SO_RCVMARK 75 139 139 140 + #define SO_PASSPIDFD 76 141 + 140 142 #if !defined(__KERNEL__) 141 143 142 144 #if __BITS_PER_LONG == 64
+2
arch/mips/include/uapi/asm/socket.h
··· 148 148 149 149 #define SO_RCVMARK 75 150 150 151 + #define SO_PASSPIDFD 76 152 + 151 153 #if !defined(__KERNEL__) 152 154 153 155 #if __BITS_PER_LONG == 64
+2
arch/parisc/include/uapi/asm/socket.h
··· 129 129 130 130 #define SO_RCVMARK 0x4049 131 131 132 + #define SO_PASSPIDFD 0x404A 133 + 132 134 #if !defined(__KERNEL__) 133 135 134 136 #if __BITS_PER_LONG == 64
+2
arch/sparc/include/uapi/asm/socket.h
··· 130 130 131 131 #define SO_RCVMARK 0x0054 132 132 133 + #define SO_PASSPIDFD 0x0055 134 + 133 135 #if !defined(__KERNEL__) 134 136 135 137
+1
include/linux/net.h
··· 43 43 #define SOCK_PASSSEC 4 44 44 #define SOCK_SUPPORT_ZC 5 45 45 #define SOCK_CUSTOM_SOCKOPT 6 46 + #define SOCK_PASSPIDFD 7 46 47 47 48 #ifndef ARCH_HAS_SOCKET_TYPES 48 49 /**
+1
include/linux/socket.h
··· 177 177 #define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */ 178 178 #define SCM_CREDENTIALS 0x02 /* rw: struct ucred */ 179 179 #define SCM_SECURITY 0x03 /* rw: security label */ 180 + #define SCM_PIDFD 0x04 /* ro: pidfd (int) */ 180 181 181 182 struct ucred { 182 183 __u32 pid;
+37 -2
include/net/scm.h
··· 120 120 } 121 121 #endif /* CONFIG_SECURITY_NETWORK */ 122 122 123 + static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm) 124 + { 125 + struct file *pidfd_file = NULL; 126 + int pidfd; 127 + 128 + /* 129 + * put_cmsg() doesn't return an error if CMSG is truncated, 130 + * that's why we need to opencode these checks here. 131 + */ 132 + if ((msg->msg_controllen <= sizeof(struct cmsghdr)) || 133 + (msg->msg_controllen - sizeof(struct cmsghdr)) < sizeof(int)) { 134 + msg->msg_flags |= MSG_CTRUNC; 135 + return; 136 + } 137 + 138 + WARN_ON_ONCE(!scm->pid); 139 + pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file); 140 + 141 + if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) { 142 + if (pidfd_file) { 143 + put_unused_fd(pidfd); 144 + fput(pidfd_file); 145 + } 146 + 147 + return; 148 + } 149 + 150 + if (pidfd_file) 151 + fd_install(pidfd, pidfd_file); 152 + } 153 + 123 154 static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg, 124 155 struct scm_cookie *scm, int flags) 125 156 { 126 157 if (!msg->msg_control) { 127 - if (test_bit(SOCK_PASSCRED, &sock->flags) || scm->fp || 128 - scm_has_secdata(sock)) 158 + if (test_bit(SOCK_PASSCRED, &sock->flags) || 159 + test_bit(SOCK_PASSPIDFD, &sock->flags) || 160 + scm->fp || scm_has_secdata(sock)) 129 161 msg->msg_flags |= MSG_CTRUNC; 130 162 scm_destroy(scm); 131 163 return; ··· 172 140 }; 173 141 put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds); 174 142 } 143 + 144 + if (test_bit(SOCK_PASSPIDFD, &sock->flags)) 145 + scm_pidfd_recv(msg, scm); 175 146 176 147 scm_destroy_cred(scm); 177 148
+2
include/uapi/asm-generic/socket.h
··· 132 132 133 133 #define SO_RCVMARK 75 134 134 135 + #define SO_PASSPIDFD 76 136 + 135 137 #if !defined(__KERNEL__) 136 138 137 139 #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+11
net/core/sock.c
··· 1246 1246 clear_bit(SOCK_PASSCRED, &sock->flags); 1247 1247 break; 1248 1248 1249 + case SO_PASSPIDFD: 1250 + if (valbool) 1251 + set_bit(SOCK_PASSPIDFD, &sock->flags); 1252 + else 1253 + clear_bit(SOCK_PASSPIDFD, &sock->flags); 1254 + break; 1255 + 1249 1256 case SO_TIMESTAMP_OLD: 1250 1257 case SO_TIMESTAMP_NEW: 1251 1258 case SO_TIMESTAMPNS_OLD: ··· 1737 1730 1738 1731 case SO_PASSCRED: 1739 1732 v.val = !!test_bit(SOCK_PASSCRED, &sock->flags); 1733 + break; 1734 + 1735 + case SO_PASSPIDFD: 1736 + v.val = !!test_bit(SOCK_PASSPIDFD, &sock->flags); 1740 1737 break; 1741 1738 1742 1739 case SO_PEERCRED:
+1
net/mptcp/sockopt.c
··· 355 355 case SO_BROADCAST: 356 356 case SO_BSDCOMPAT: 357 357 case SO_PASSCRED: 358 + case SO_PASSPIDFD: 358 359 case SO_PASSSEC: 359 360 case SO_RXQ_OVFL: 360 361 case SO_WIFI_STATUS:
+13 -5
net/unix/af_unix.c
··· 1361 1361 if (err) 1362 1362 goto out; 1363 1363 1364 - if (test_bit(SOCK_PASSCRED, &sock->flags) && 1364 + if ((test_bit(SOCK_PASSCRED, &sock->flags) || 1365 + test_bit(SOCK_PASSPIDFD, &sock->flags)) && 1365 1366 !unix_sk(sk)->addr) { 1366 1367 err = unix_autobind(sk); 1367 1368 if (err) ··· 1470 1469 if (err) 1471 1470 goto out; 1472 1471 1473 - if (test_bit(SOCK_PASSCRED, &sock->flags) && !u->addr) { 1472 + if ((test_bit(SOCK_PASSCRED, &sock->flags) || 1473 + test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) { 1474 1474 err = unix_autobind(sk); 1475 1475 if (err) 1476 1476 goto out; ··· 1672 1670 { 1673 1671 if (test_bit(SOCK_PASSCRED, &old->flags)) 1674 1672 set_bit(SOCK_PASSCRED, &new->flags); 1673 + if (test_bit(SOCK_PASSPIDFD, &old->flags)) 1674 + set_bit(SOCK_PASSPIDFD, &new->flags); 1675 1675 if (test_bit(SOCK_PASSSEC, &old->flags)) 1676 1676 set_bit(SOCK_PASSSEC, &new->flags); 1677 1677 } ··· 1823 1819 const struct sock *other) 1824 1820 { 1825 1821 return test_bit(SOCK_PASSCRED, &sock->flags) || 1822 + test_bit(SOCK_PASSPIDFD, &sock->flags) || 1826 1823 !other->sk_socket || 1827 - test_bit(SOCK_PASSCRED, &other->sk_socket->flags); 1824 + test_bit(SOCK_PASSCRED, &other->sk_socket->flags) || 1825 + test_bit(SOCK_PASSPIDFD, &other->sk_socket->flags); 1828 1826 } 1829 1827 1830 1828 /* ··· 1910 1904 goto out; 1911 1905 } 1912 1906 1913 - if (test_bit(SOCK_PASSCRED, &sock->flags) && !u->addr) { 1907 + if ((test_bit(SOCK_PASSCRED, &sock->flags) || 1908 + test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) { 1914 1909 err = unix_autobind(sk); 1915 1910 if (err) 1916 1911 goto out; ··· 2725 2718 /* Never glue messages from different writers */ 2726 2719 if (!unix_skb_scm_eq(skb, &scm)) 2727 2720 break; 2728 - } else if (test_bit(SOCK_PASSCRED, &sock->flags)) { 2721 + } else if (test_bit(SOCK_PASSCRED, &sock->flags) || 2722 + test_bit(SOCK_PASSPIDFD, &sock->flags)) { 2729 2723 /* Copy credentials */ 2730 2724 scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid); 2731 2725 unix_set_secdata(&scm, skb);
+2
tools/include/uapi/asm-generic/socket.h
··· 121 121 122 122 #define SO_RCVMARK 75 123 123 124 + #define SO_PASSPIDFD 76 125 + 124 126 #if !defined(__KERNEL__) 125 127 126 128 #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))