* [PATCH v1 net-next 1/7] af_unix: Factorise test_bit() for SOCK_PASSCRED and SOCK_PASSPIDFD.
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 2/7] af_unix: Don't pass struct socket to maybe_add_creds() Kuniyuki Iwashima
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
Currently, the same checks for SOCK_PASSCRED and SOCK_PASSPIDFD
are scattered across many places.
Let's centralise the bit tests to make the following changes cleaner.
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
net/unix/af_unix.c | 37 +++++++++++++++----------------------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 2ab20821d6bb..6dbe866f5e24 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -765,6 +765,14 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
spin_unlock(&sk->sk_peer_lock);
}
+static bool unix_passcred_enabled(const struct sock *sk)
+{
+ struct socket *sock = sk->sk_socket;
+
+ return test_bit(SOCK_PASSCRED, &sock->flags) ||
+ test_bit(SOCK_PASSPIDFD, &sock->flags);
+}
+
static int unix_listen(struct socket *sock, int backlog)
{
int err;
@@ -1411,9 +1419,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
if (err)
goto out;
- if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
- !READ_ONCE(unix_sk(sk)->addr)) {
+ if (unix_passcred_enabled(sk) && !READ_ONCE(unix_sk(sk)->addr)) {
err = unix_autobind(sk);
if (err)
goto out;
@@ -1531,9 +1537,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
if (err)
goto out;
- if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
- !READ_ONCE(u->addr)) {
+ if (unix_passcred_enabled(sk) && !READ_ONCE(u->addr)) {
err = unix_autobind(sk);
if (err)
goto out;
@@ -1877,16 +1881,6 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
return err;
}
-static bool unix_passcred_enabled(const struct socket *sock,
- const struct sock *other)
-{
- return test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags) ||
- !other->sk_socket ||
- test_bit(SOCK_PASSCRED, &other->sk_socket->flags) ||
- test_bit(SOCK_PASSPIDFD, &other->sk_socket->flags);
-}
-
/*
* Some apps rely on write() giving SCM_CREDENTIALS
* We include credentials if source or destination socket
@@ -1897,7 +1891,9 @@ static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
{
if (UNIXCB(skb).pid)
return;
- if (unix_passcred_enabled(sock, other)) {
+
+ if (unix_passcred_enabled(sock->sk) ||
+ !other->sk_socket || unix_passcred_enabled(other)) {
UNIXCB(skb).pid = get_pid(task_tgid(current));
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
}
@@ -1974,9 +1970,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
goto out;
}
- if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
- !READ_ONCE(u->addr)) {
+ if (unix_passcred_enabled(sk) && !READ_ONCE(u->addr)) {
err = unix_autobind(sk);
if (err)
goto out;
@@ -2846,8 +2840,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
/* Never glue messages from different writers */
if (!unix_skb_scm_eq(skb, &scm))
break;
- } else if (test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags)) {
+ } else if (unix_passcred_enabled(sk)) {
/* Copy credentials */
scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
unix_set_secdata(&scm, skb);
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v1 net-next 2/7] af_unix: Don't pass struct socket to maybe_add_creds().
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 1/7] af_unix: Factorise test_bit() for SOCK_PASSCRED and SOCK_PASSPIDFD Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 3/7] scm: Move scm_recv() from scm.h to scm.c Kuniyuki Iwashima
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
We will move SOCK_PASS{CRED,PIDFD,SEC} from struct socket.flags
to struct sock.sk_flags for better handling with SOCK_PASSRIGHTS.
Then, we don't need to access struct socket in maybe_add_creds().
Let's pass struct sock to maybe_add_creds() and its caller
queue_oob().
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
net/unix/af_unix.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 6dbe866f5e24..fa86e8c3aec5 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1886,13 +1886,13 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
* We include credentials if source or destination socket
* asserted SOCK_PASSCRED.
*/
-static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
+static void maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
const struct sock *other)
{
if (UNIXCB(skb).pid)
return;
- if (unix_passcred_enabled(sock->sk) ||
+ if (unix_passcred_enabled(sk) ||
!other->sk_socket || unix_passcred_enabled(other)) {
UNIXCB(skb).pid = get_pid(task_tgid(current));
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
@@ -2133,7 +2133,8 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (sock_flag(other, SOCK_RCVTSTAMP))
__net_timestamp(skb);
- maybe_add_creds(skb, sock, other);
+
+ maybe_add_creds(skb, sk, other);
scm_stat_add(other, skb);
skb_queue_tail(&other->sk_receive_queue, skb);
unix_state_unlock(other);
@@ -2161,14 +2162,14 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
#define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768))
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
-static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other,
+static int queue_oob(struct sock *sk, struct msghdr *msg, struct sock *other,
struct scm_cookie *scm, bool fds_sent)
{
struct unix_sock *ousk = unix_sk(other);
struct sk_buff *skb;
int err;
- skb = sock_alloc_send_skb(sock->sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);
+ skb = sock_alloc_send_skb(sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return err;
@@ -2192,7 +2193,7 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other
goto out;
}
- maybe_add_creds(skb, sock, other);
+ maybe_add_creds(skb, sk, other);
scm_stat_add(other, skb);
spin_lock(&other->sk_receive_queue.lock);
@@ -2308,7 +2309,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
(other->sk_shutdown & RCV_SHUTDOWN))
goto out_pipe_unlock;
- maybe_add_creds(skb, sock, other);
+ maybe_add_creds(skb, sk, other);
scm_stat_add(other, skb);
skb_queue_tail(&other->sk_receive_queue, skb);
unix_state_unlock(other);
@@ -2318,7 +2319,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
if (msg->msg_flags & MSG_OOB) {
- err = queue_oob(sock, msg, other, &scm, fds_sent);
+ err = queue_oob(sk, msg, other, &scm, fds_sent);
if (err)
goto out_err;
sent++;
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v1 net-next 3/7] scm: Move scm_recv() from scm.h to scm.c.
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 1/7] af_unix: Factorise test_bit() for SOCK_PASSCRED and SOCK_PASSPIDFD Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 2/7] af_unix: Don't pass struct socket to maybe_add_creds() Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 4/7] af_unix: Move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags Kuniyuki Iwashima
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
scm_recv() has been placed in scm.h since the pre-git era for no
particular reason (I think), which makes the file really fragile.
For example, when you move SOCK_PASSCRED from include/linux/net.h to
enum sock_flags in include/net/sock.h, you will see weird build failure
due to terrible dependency.
The next patch moves SOCK_PASSXXX to sk->sk_flags to better handle the
new flag for SO_PASSRIGHTS.
To avoid the build failure, let's move scm_recv(_unix())? and its
callees to scm.c.
Note that only scm_recv() needs to be exported for Bluetooth.
scm_send() should be moved to scm.c too, but I'll revisit later.
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
include/net/scm.h | 121 ++-------------------------------------------
net/core/scm.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 126 insertions(+), 117 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 22bb49589fde..84c4707e78a5 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -102,123 +102,10 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
return __scm_send(sock, msg, scm);
}
-#ifdef CONFIG_SECURITY_NETWORK
-static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
-{
- struct lsm_context ctx;
- int err;
-
- if (test_bit(SOCK_PASSSEC, &sock->flags)) {
- err = security_secid_to_secctx(scm->secid, &ctx);
-
- if (err >= 0) {
- put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
- ctx.context);
- security_release_secctx(&ctx);
- }
- }
-}
-
-static inline bool scm_has_secdata(struct socket *sock)
-{
- return test_bit(SOCK_PASSSEC, &sock->flags);
-}
-#else
-static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
-{ }
-
-static inline bool scm_has_secdata(struct socket *sock)
-{
- return false;
-}
-#endif /* CONFIG_SECURITY_NETWORK */
-
-static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
-{
- struct file *pidfd_file = NULL;
- int len, pidfd;
-
- /* put_cmsg() doesn't return an error if CMSG is truncated,
- * that's why we need to opencode these checks here.
- */
- if (msg->msg_flags & MSG_CMSG_COMPAT)
- len = sizeof(struct compat_cmsghdr) + sizeof(int);
- else
- len = sizeof(struct cmsghdr) + sizeof(int);
-
- if (msg->msg_controllen < len) {
- msg->msg_flags |= MSG_CTRUNC;
- return;
- }
-
- if (!scm->pid)
- return;
-
- pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file);
-
- if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
- if (pidfd_file) {
- put_unused_fd(pidfd);
- fput(pidfd_file);
- }
-
- return;
- }
-
- if (pidfd_file)
- fd_install(pidfd, pidfd_file);
-}
-
-static inline bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm, int flags)
-{
- if (!msg->msg_control) {
- if (test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags) ||
- scm->fp || scm_has_secdata(sock))
- msg->msg_flags |= MSG_CTRUNC;
- scm_destroy(scm);
- return false;
- }
-
- if (test_bit(SOCK_PASSCRED, &sock->flags)) {
- struct user_namespace *current_ns = current_user_ns();
- struct ucred ucreds = {
- .pid = scm->creds.pid,
- .uid = from_kuid_munged(current_ns, scm->creds.uid),
- .gid = from_kgid_munged(current_ns, scm->creds.gid),
- };
- put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
- }
-
- scm_passec(sock, msg, scm);
-
- if (scm->fp)
- scm_detach_fds(msg, scm);
-
- return true;
-}
-
-static inline void scm_recv(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm, int flags)
-{
- if (!__scm_recv_common(sock, msg, scm, flags))
- return;
-
- scm_destroy_cred(scm);
-}
-
-static inline void scm_recv_unix(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm, int flags)
-{
- if (!__scm_recv_common(sock, msg, scm, flags))
- return;
-
- if (test_bit(SOCK_PASSPIDFD, &sock->flags))
- scm_pidfd_recv(msg, scm);
-
- scm_destroy_cred(scm);
-}
+void scm_recv(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm, int flags);
+void scm_recv_unix(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm, int flags);
static inline int scm_recv_one_fd(struct file *f, int __user *ufd,
unsigned int flags)
diff --git a/net/core/scm.c b/net/core/scm.c
index 733c0cbd393d..3f756f00e41e 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -404,3 +404,125 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
return new_fpl;
}
EXPORT_SYMBOL(scm_fp_dup);
+
+#ifdef CONFIG_SECURITY_NETWORK
+static void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
+{
+ struct lsm_context ctx;
+ int err;
+
+ if (test_bit(SOCK_PASSSEC, &sock->flags)) {
+ err = security_secid_to_secctx(scm->secid, &ctx);
+
+ if (err >= 0) {
+ put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
+ ctx.context);
+
+ security_release_secctx(&ctx);
+ }
+ }
+}
+
+static bool scm_has_secdata(struct socket *sock)
+{
+ return test_bit(SOCK_PASSSEC, &sock->flags);
+}
+#else
+static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
+{ }
+
+static inline bool scm_has_secdata(struct socket *sock)
+{
+ return false;
+}
+#endif
+
+static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
+{
+ struct file *pidfd_file = NULL;
+ int len, pidfd;
+
+ /* put_cmsg() doesn't return an error if CMSG is truncated,
+ * that's why we need to opencode these checks here.
+ */
+ if (msg->msg_flags & MSG_CMSG_COMPAT)
+ len = sizeof(struct compat_cmsghdr) + sizeof(int);
+ else
+ len = sizeof(struct cmsghdr) + sizeof(int);
+
+ if (msg->msg_controllen < len) {
+ msg->msg_flags |= MSG_CTRUNC;
+ return;
+ }
+
+ if (!scm->pid)
+ return;
+
+ pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file);
+
+ if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
+ if (pidfd_file) {
+ put_unused_fd(pidfd);
+ fput(pidfd_file);
+ }
+
+ return;
+ }
+
+ if (pidfd_file)
+ fd_install(pidfd, pidfd_file);
+}
+
+static bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm, int flags)
+{
+ if (!msg->msg_control) {
+ if (test_bit(SOCK_PASSCRED, &sock->flags) ||
+ test_bit(SOCK_PASSPIDFD, &sock->flags) ||
+ scm->fp || scm_has_secdata(sock))
+ msg->msg_flags |= MSG_CTRUNC;
+
+ scm_destroy(scm);
+ return false;
+ }
+
+ if (test_bit(SOCK_PASSCRED, &sock->flags)) {
+ struct user_namespace *current_ns = current_user_ns();
+ struct ucred ucreds = {
+ .pid = scm->creds.pid,
+ .uid = from_kuid_munged(current_ns, scm->creds.uid),
+ .gid = from_kgid_munged(current_ns, scm->creds.gid),
+ };
+
+ put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
+ }
+
+ scm_passec(sock, msg, scm);
+
+ if (scm->fp)
+ scm_detach_fds(msg, scm);
+
+ return true;
+}
+
+void scm_recv(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm, int flags)
+{
+ if (!__scm_recv_common(sock, msg, scm, flags))
+ return;
+
+ scm_destroy_cred(scm);
+}
+EXPORT_SYMBOL(scm_recv);
+
+void scm_recv_unix(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm, int flags)
+{
+ if (!__scm_recv_common(sock, msg, scm, flags))
+ return;
+
+ if (test_bit(SOCK_PASSPIDFD, &sock->flags))
+ scm_pidfd_recv(msg, scm);
+
+ scm_destroy_cred(scm);
+}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v1 net-next 4/7] af_unix: Move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags.
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
` (2 preceding siblings ...)
2025-05-08 1:29 ` [PATCH v1 net-next 3/7] scm: Move scm_recv() from scm.h to scm.c Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
2025-05-09 3:15 ` Jakub Kicinski
2025-05-08 1:29 ` [PATCH v1 net-next 5/7] af_unix: Inherit sk_flags at connect() Kuniyuki Iwashima
` (2 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
As explained in the next patch, SO_PASSRIGHTS would have a problem
if we assigned a corresponding bit to socket->flags, so it must be
flagged on sk->sk_flags.
Mixing socket->flags and sk->sk_flags for similar options will look
confusing, and sk->sk_flags is unsigned long and has enough space.
Let's move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags.
While at it, BUILD_BUG_ON() is added in sock_set_flag(), and other
SOCK_XXX flags in net.h are grouped as enum.
Now, we can drop unix_sock_inherit_flags() and use sock_copy_flags()
instead to inherit flags from a listener to its embryo socket.
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
include/linux/net.h | 15 +++++++--------
include/net/sock.h | 5 +++++
net/core/scm.c | 14 ++++++++------
net/core/sock.c | 12 ++++++------
net/unix/af_unix.c | 19 +++----------------
5 files changed, 29 insertions(+), 36 deletions(-)
diff --git a/include/linux/net.h b/include/linux/net.h
index 0ff950eecc6b..f8418d6e33e0 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -36,14 +36,13 @@ struct net;
* in sock->flags, but moved into sk->sk_wq->flags to be RCU protected.
* Eventually all flags will be in sk->sk_wq->flags.
*/
-#define SOCKWQ_ASYNC_NOSPACE 0
-#define SOCKWQ_ASYNC_WAITDATA 1
-#define SOCK_NOSPACE 2
-#define SOCK_PASSCRED 3
-#define SOCK_PASSSEC 4
-#define SOCK_SUPPORT_ZC 5
-#define SOCK_CUSTOM_SOCKOPT 6
-#define SOCK_PASSPIDFD 7
+enum socket_flags {
+ SOCKWQ_ASYNC_NOSPACE,
+ SOCKWQ_ASYNC_WAITDATA,
+ SOCK_NOSPACE,
+ SOCK_SUPPORT_ZC,
+ SOCK_CUSTOM_SOCKOPT,
+};
#ifndef ARCH_HAS_SOCKET_TYPES
/**
diff --git a/include/net/sock.h b/include/net/sock.h
index f0fabb9fd28a..48b8856e2615 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -964,6 +964,10 @@ enum sock_flags {
SOCK_RCVMARK, /* Receive SO_MARK ancillary data with packet */
SOCK_RCVPRIORITY, /* Receive SO_PRIORITY ancillary data with packet */
SOCK_TIMESTAMPING_ANY, /* Copy of sk_tsflags & TSFLAGS_ANY */
+ SOCK_PASSCRED, /* Receive SCM_CREDENTIALS ancillary data with packet */
+ SOCK_PASSPIDFD, /* Receive SCM_PIDFD ancillary data with packet */
+ SOCK_PASSSEC, /* Receive SCM_SECURITY ancillary data with packet */
+ SOCK_FLAG_MAX,
};
#define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
@@ -981,6 +985,7 @@ static inline void sock_copy_flags(struct sock *nsk, const struct sock *osk)
static inline void sock_set_flag(struct sock *sk, enum sock_flags flag)
{
+ BUILD_BUG_ON(BYTES_TO_BITS(sizeof(sk->sk_flags)) <= SOCK_FLAG_MAX);
__set_bit(flag, &sk->sk_flags);
}
diff --git a/net/core/scm.c b/net/core/scm.c
index 3f756f00e41e..4bf19f6303dd 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -411,7 +411,7 @@ static void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cooki
struct lsm_context ctx;
int err;
- if (test_bit(SOCK_PASSSEC, &sock->flags)) {
+ if (sock_flag(sock->sk, SOCK_PASSSEC)) {
err = security_secid_to_secctx(scm->secid, &ctx);
if (err >= 0) {
@@ -425,7 +425,7 @@ static void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cooki
static bool scm_has_secdata(struct socket *sock)
{
- return test_bit(SOCK_PASSSEC, &sock->flags);
+ return sock_flag(sock->sk, SOCK_PASSSEC);
}
#else
static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
@@ -476,9 +476,11 @@ static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
static bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
+ struct sock *sk = sock->sk;
+
if (!msg->msg_control) {
- if (test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags) ||
+ if (sock_flag(sk, SOCK_PASSCRED) ||
+ sock_flag(sk, SOCK_PASSPIDFD) ||
scm->fp || scm_has_secdata(sock))
msg->msg_flags |= MSG_CTRUNC;
@@ -486,7 +488,7 @@ static bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
return false;
}
- if (test_bit(SOCK_PASSCRED, &sock->flags)) {
+ if (sock_flag(sock->sk, SOCK_PASSCRED)) {
struct user_namespace *current_ns = current_user_ns();
struct ucred ucreds = {
.pid = scm->creds.pid,
@@ -521,7 +523,7 @@ void scm_recv_unix(struct socket *sock, struct msghdr *msg,
if (!__scm_recv_common(sock, msg, scm, flags))
return;
- if (test_bit(SOCK_PASSPIDFD, &sock->flags))
+ if (sock_flag(sock->sk, SOCK_PASSPIDFD))
scm_pidfd_recv(msg, scm);
scm_destroy_cred(scm);
diff --git a/net/core/sock.c b/net/core/sock.c
index b64df2463300..a1720c7f9789 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1221,13 +1221,13 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
}
return -EPERM;
case SO_PASSSEC:
- assign_bit(SOCK_PASSSEC, &sock->flags, valbool);
+ sock_valbool_flag(sk, SOCK_PASSSEC, valbool);
return 0;
case SO_PASSCRED:
- assign_bit(SOCK_PASSCRED, &sock->flags, valbool);
+ sock_valbool_flag(sk, SOCK_PASSCRED, valbool);
return 0;
case SO_PASSPIDFD:
- assign_bit(SOCK_PASSPIDFD, &sock->flags, valbool);
+ sock_valbool_flag(sk, SOCK_PASSPIDFD, valbool);
return 0;
case SO_TYPE:
case SO_PROTOCOL:
@@ -1853,11 +1853,11 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
break;
case SO_PASSCRED:
- v.val = !!test_bit(SOCK_PASSCRED, &sock->flags);
+ v.val = sock_flag(sk, SOCK_PASSCRED);
break;
case SO_PASSPIDFD:
- v.val = !!test_bit(SOCK_PASSPIDFD, &sock->flags);
+ v.val = sock_flag(sk, SOCK_PASSPIDFD);
break;
case SO_PEERCRED:
@@ -1954,7 +1954,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
break;
case SO_PASSSEC:
- v.val = !!test_bit(SOCK_PASSSEC, &sock->flags);
+ v.val = sock_flag(sk, SOCK_PASSSEC);
break;
case SO_PEERSEC:
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index fa86e8c3aec5..e793e55f6c9b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -767,10 +767,8 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
static bool unix_passcred_enabled(const struct sock *sk)
{
- struct socket *sock = sk->sk_socket;
-
- return test_bit(SOCK_PASSCRED, &sock->flags) ||
- test_bit(SOCK_PASSPIDFD, &sock->flags);
+ return sock_flag(sk, SOCK_PASSCRED) ||
+ sock_flag(sk, SOCK_PASSPIDFD);
}
static int unix_listen(struct socket *sock, int backlog)
@@ -1713,17 +1711,6 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
return 0;
}
-static void unix_sock_inherit_flags(const struct socket *old,
- struct socket *new)
-{
- if (test_bit(SOCK_PASSCRED, &old->flags))
- set_bit(SOCK_PASSCRED, &new->flags);
- if (test_bit(SOCK_PASSPIDFD, &old->flags))
- set_bit(SOCK_PASSPIDFD, &new->flags);
- if (test_bit(SOCK_PASSSEC, &old->flags))
- set_bit(SOCK_PASSSEC, &new->flags);
-}
-
static int unix_accept(struct socket *sock, struct socket *newsock,
struct proto_accept_arg *arg)
{
@@ -1760,7 +1747,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock,
unix_state_lock(tsk);
unix_update_edges(unix_sk(tsk));
newsock->state = SS_CONNECTED;
- unix_sock_inherit_flags(sock, newsock);
+ sock_copy_flags(tsk, sk);
sock_graft(tsk, newsock);
unix_state_unlock(tsk);
return 0;
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v1 net-next 4/7] af_unix: Move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags.
2025-05-08 1:29 ` [PATCH v1 net-next 4/7] af_unix: Move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags Kuniyuki Iwashima
@ 2025-05-09 3:15 ` Jakub Kicinski
2025-05-09 4:33 ` Kuniyuki Iwashima
0 siblings, 1 reply; 10+ messages in thread
From: Jakub Kicinski @ 2025-05-09 3:15 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Willem de Bruijn,
Simon Horman, Christian Brauner, Kuniyuki Iwashima, netdev
On Wed, 7 May 2025 18:29:16 -0700 Kuniyuki Iwashima wrote:
> diff --git a/include/net/sock.h b/include/net/sock.h
> index f0fabb9fd28a..48b8856e2615 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -964,6 +964,10 @@ enum sock_flags {
> SOCK_RCVMARK, /* Receive SO_MARK ancillary data with packet */
> SOCK_RCVPRIORITY, /* Receive SO_PRIORITY ancillary data with packet */
> SOCK_TIMESTAMPING_ANY, /* Copy of sk_tsflags & TSFLAGS_ANY */
> + SOCK_PASSCRED, /* Receive SCM_CREDENTIALS ancillary data with packet */
> + SOCK_PASSPIDFD, /* Receive SCM_PIDFD ancillary data with packet */
> + SOCK_PASSSEC, /* Receive SCM_SECURITY ancillary data with packet */
> + SOCK_FLAG_MAX,
> };
32b builds break:
include/linux/compiler_types.h:557:45: error: call to ‘__compiletime_assert_809’ declared with attribute error: BUILD_BUG_ON failed: BYTES_TO_BITS(sizeof(sk->sk_flags)) <= SOCK_FLAG_MAX
--
pw-bot: cr
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v1 net-next 4/7] af_unix: Move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags.
2025-05-09 3:15 ` Jakub Kicinski
@ 2025-05-09 4:33 ` Kuniyuki Iwashima
0 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-09 4:33 UTC (permalink / raw)
To: kuba
Cc: brauner, davem, edumazet, horms, kuni1840, kuniyu, netdev, pabeni,
willemb
From: Jakub Kicinski <kuba@kernel.org>
Date: Thu, 8 May 2025 20:15:02 -0700
> On Wed, 7 May 2025 18:29:16 -0700 Kuniyuki Iwashima wrote:
> > diff --git a/include/net/sock.h b/include/net/sock.h
> > index f0fabb9fd28a..48b8856e2615 100644
> > --- a/include/net/sock.h
> > +++ b/include/net/sock.h
> > @@ -964,6 +964,10 @@ enum sock_flags {
> > SOCK_RCVMARK, /* Receive SO_MARK ancillary data with packet */
> > SOCK_RCVPRIORITY, /* Receive SO_PRIORITY ancillary data with packet */
> > SOCK_TIMESTAMPING_ANY, /* Copy of sk_tsflags & TSFLAGS_ANY */
> > + SOCK_PASSCRED, /* Receive SCM_CREDENTIALS ancillary data with packet */
> > + SOCK_PASSPIDFD, /* Receive SCM_PIDFD ancillary data with packet */
> > + SOCK_PASSSEC, /* Receive SCM_SECURITY ancillary data with packet */
> > + SOCK_FLAG_MAX,
> > };
>
> 32b builds break:
>
> include/linux/compiler_types.h:557:45: error: call to ‘__compiletime_assert_809’ declared with attribute error: BUILD_BUG_ON failed: BYTES_TO_BITS(sizeof(sk->sk_flags)) <= SOCK_FLAG_MAX
Oops, thanks for catching!
Will create a space like this in v2.
---8<---
diff --git a/include/net/sock.h b/include/net/sock.h
index 371053316d2c..59c077df9eb8 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -521,8 +521,9 @@ struct sock {
#if BITS_PER_LONG==32
seqlock_t sk_stamp_seq;
#endif
- int sk_disconnects;
+ u16 sk_disconnects;
+ u8 sk_csm_flags;
u8 sk_txrehash;
u8 sk_clockid;
u8 sk_txtime_deadline_mode : 1,
---8<---
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 net-next 5/7] af_unix: Inherit sk_flags at connect().
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
` (3 preceding siblings ...)
2025-05-08 1:29 ` [PATCH v1 net-next 4/7] af_unix: Move SOCK_PASS{CRED,PIDFD,SEC} to sk->sk_flags Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 6/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 7/7] selftest: af_unix: Test SO_PASSRIGHTS Kuniyuki Iwashima
6 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
For SOCK_STREAM embryo sockets, the SO_PASS{CRED,PIDFD,SEC} options
are inherited from the parent listen()ing socket.
Currently, this inheritance happens at accept(), because these
attributes were stored in sk->sk_socket->flags and the struct socket
is not allocated until accept().
This leads to unintentional behaviour.
When a peer sends data to an embryo socket in the accept() queue,
maybe_add_creds() embeds credentials into the skb, even if neither
the peer nor the listener has enabled these options.
If the option is enabled, the embryo socket receives the ancillary
data after accept(). If not, the data is silently discarded.
This conservative approach works for SO_PASS{CRED,PIDFD,SEC}, but not
for SO_PASSRIGHTS; once an SCM_RIGHTS with a hung file descriptor is
sent, it’s game over.
To avoid this, we will need to preserve SOCK_PASSRIGHTS even on embryo
sockets.
A recent change made it possible to access the parent's flags in
sendmsg() via unix_sk(other)->listener->sk->sk_socket->flags, but
this introduces an unnecessary condition that is irrelevant for
most sockets (i.e., accepted sockets and clients).
Therefore, we moved SOCK_PASSXXX into sk->sk_flags, which does not
depend on struct socket.
Let’s inherit sk->sk_flags at connect() to avoid receiving SCM_RIGHTS
on embryo sockets created from a parent with SO_PASSRIGHTS=0.
While at it, whitespace issues around pid assignment have been fixed.
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
net/unix/af_unix.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e793e55f6c9b..daa7a8ead243 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1631,6 +1631,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
newsk->sk_state = TCP_ESTABLISHED;
newsk->sk_type = sk->sk_type;
init_peercred(newsk);
+ sock_copy_flags(newsk, other);
newu = unix_sk(newsk);
newu->listener = other;
RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
@@ -1747,7 +1748,6 @@ static int unix_accept(struct socket *sock, struct socket *newsock,
unix_state_lock(tsk);
unix_update_edges(unix_sk(tsk));
newsock->state = SS_CONNECTED;
- sock_copy_flags(tsk, sk);
sock_graft(tsk, newsock);
unix_state_unlock(tsk);
return 0;
@@ -1856,7 +1856,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
{
int err = 0;
- UNIXCB(skb).pid = get_pid(scm->pid);
+ UNIXCB(skb).pid = get_pid(scm->pid);
UNIXCB(skb).uid = scm->creds.uid;
UNIXCB(skb).gid = scm->creds.gid;
UNIXCB(skb).fp = NULL;
@@ -1879,9 +1879,8 @@ static void maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
if (UNIXCB(skb).pid)
return;
- if (unix_passcred_enabled(sk) ||
- !other->sk_socket || unix_passcred_enabled(other)) {
- UNIXCB(skb).pid = get_pid(task_tgid(current));
+ if (unix_passcred_enabled(sk) || unix_passcred_enabled(other)) {
+ UNIXCB(skb).pid = get_pid(task_tgid(current));
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
}
}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v1 net-next 6/7] af_unix: Introduce SO_PASSRIGHTS.
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
` (4 preceding siblings ...)
2025-05-08 1:29 ` [PATCH v1 net-next 5/7] af_unix: Inherit sk_flags at connect() Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
2025-05-08 1:29 ` [PATCH v1 net-next 7/7] selftest: af_unix: Test SO_PASSRIGHTS Kuniyuki Iwashima
6 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
As long as recvmsg() or recvmmsg() is used with cmsg, it is not
possible to avoid receiving file descriptors via SCM_RIGHTS.
This behaviour has occasionally been flagged as problematic, as
it can be (ab)used to trigger DoS during close(), for example, by
passing a FUSE-controlled fd or a hung NFS fd.
For instance, as noted on the uAPI Group page [0], an untrusted peer
could send a file descriptor pointing to a hung NFS mount and then
close it. Once the receiver calls recvmsg() with msg_control, the
descriptor is automatically installed, and then the responsibility
for the final close() now falls on the receiver, which may result
in blocking the process for a long time.
Regarding this, systemd calls cmsg_close_all() [1] after each
recvmsg() to close() unwanted file descriptors sent via SCM_RIGHTS.
However, this cannot work around the issue at all, because the final
fput() may still occur on the receiver's side once sendmsg() with
SCM_RIGHTS succeeds. Also, even filtering by LSM at recvmsg() does
not work for the same reason.
Thus, we need a better way to refuse SCM_RIGHTS at sendmsg().
Let's introduce SO_PASSRIGHTS to disable SCM_RIGHTS.
Note that this option is enabled by default for backward
compatibility.
Link: https://uapi-group.org/kernel-features/#disabling-reception-of-scm_rights-for-af_unix-sockets #[0]
Link: https://github.com/systemd/systemd/blob/v257.5/src/basic/fd-util.c#L612-L628 #[1]
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
arch/alpha/include/uapi/asm/socket.h | 2 ++
arch/mips/include/uapi/asm/socket.h | 2 ++
arch/parisc/include/uapi/asm/socket.h | 2 ++
arch/sparc/include/uapi/asm/socket.h | 2 ++
include/net/sock.h | 1 +
include/uapi/asm-generic/socket.h | 2 ++
net/core/sock.c | 13 +++++++++++++
net/unix/af_unix.c | 22 ++++++++++++++++++++--
tools/include/uapi/asm-generic/socket.h | 2 ++
9 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index 3df5f2dd4c0f..8f1f18adcdb5 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -150,6 +150,8 @@
#define SO_RCVPRIORITY 82
+#define SO_PASSRIGHTS 83
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index 22fa8f19924a..31ac655b7837 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -161,6 +161,8 @@
#define SO_RCVPRIORITY 82
+#define SO_PASSRIGHTS 83
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index 96831c988606..1f2d5b7a7f5d 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -142,6 +142,8 @@
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 0x4050
+#define SO_PASSRIGHTS 0x4051
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index 5b464a568664..adcba7329386 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -143,6 +143,8 @@
#define SO_RCVPRIORITY 0x005b
+#define SO_PASSRIGHTS 0x005c
+
#if !defined(__KERNEL__)
diff --git a/include/net/sock.h b/include/net/sock.h
index 48b8856e2615..7de988daa4a7 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -966,6 +966,7 @@ enum sock_flags {
SOCK_TIMESTAMPING_ANY, /* Copy of sk_tsflags & TSFLAGS_ANY */
SOCK_PASSCRED, /* Receive SCM_CREDENTIALS ancillary data with packet */
SOCK_PASSPIDFD, /* Receive SCM_PIDFD ancillary data with packet */
+ SOCK_PASSRIGHTS, /* Receive SCM_RIGHTS ancillary data with packet */
SOCK_PASSSEC, /* Receive SCM_SECURITY ancillary data with packet */
SOCK_FLAG_MAX,
};
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index aa5016ff3d91..f333a0ac4ee4 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -145,6 +145,8 @@
#define SO_RCVPRIORITY 82
+#define SO_PASSRIGHTS 83
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
diff --git a/net/core/sock.c b/net/core/sock.c
index a1720c7f9789..ab07cbc79d2d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1229,6 +1229,12 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
case SO_PASSPIDFD:
sock_valbool_flag(sk, SOCK_PASSPIDFD, valbool);
return 0;
+ case SO_PASSRIGHTS:
+ if (sk->sk_family != AF_UNIX)
+ return -EINVAL;
+
+ sock_valbool_flag(sk, SOCK_PASSRIGHTS, valbool);
+ return 0;
case SO_TYPE:
case SO_PROTOCOL:
case SO_DOMAIN:
@@ -1860,6 +1866,13 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
v.val = sock_flag(sk, SOCK_PASSPIDFD);
break;
+ case SO_PASSRIGHTS:
+ if (sk->sk_family != AF_UNIX)
+ return -EINVAL;
+
+ v.val = sock_flag(sk, SOCK_PASSRIGHTS);
+ break;
+
case SO_PEERCRED:
{
struct ucred peercred;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index daa7a8ead243..1f0465139066 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1015,6 +1015,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern,
}
sock_init_data(sock, sk);
+ sock_set_flag(sk, SOCK_PASSRIGHTS);
sk->sk_hash = unix_unbound_hash(sk);
sk->sk_allocation = GFP_KERNEL_ACCOUNT;
@@ -2073,6 +2074,11 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
goto out_unlock;
}
+ if (UNIXCB(skb).fp && !sock_flag(other, SOCK_PASSRIGHTS)) {
+ err = -EPERM;
+ goto out_unlock;
+ }
+
if (sk->sk_type != SOCK_SEQPACKET) {
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
if (err)
@@ -2174,9 +2180,13 @@ static int queue_oob(struct sock *sk, struct msghdr *msg, struct sock *other,
if (sock_flag(other, SOCK_DEAD) ||
(other->sk_shutdown & RCV_SHUTDOWN)) {
- unix_state_unlock(other);
err = -EPIPE;
- goto out;
+ goto out_unlock;
+ }
+
+ if (UNIXCB(skb).fp && !sock_flag(other, SOCK_PASSRIGHTS)) {
+ err = -EPERM;
+ goto out_unlock;
}
maybe_add_creds(skb, sk, other);
@@ -2192,6 +2202,8 @@ static int queue_oob(struct sock *sk, struct msghdr *msg, struct sock *other,
other->sk_data_ready(other);
return 0;
+out_unlock:
+ unix_state_unlock(other);
out:
consume_skb(skb);
return err;
@@ -2295,6 +2307,12 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
(other->sk_shutdown & RCV_SHUTDOWN))
goto out_pipe_unlock;
+ if (UNIXCB(skb).fp && !sock_flag(other, SOCK_PASSRIGHTS)) {
+ unix_state_unlock(other);
+ err = -EPERM;
+ goto out_free;
+ }
+
maybe_add_creds(skb, sk, other);
scm_stat_add(other, skb);
skb_queue_tail(&other->sk_receive_queue, skb);
diff --git a/tools/include/uapi/asm-generic/socket.h b/tools/include/uapi/asm-generic/socket.h
index aa5016ff3d91..f333a0ac4ee4 100644
--- a/tools/include/uapi/asm-generic/socket.h
+++ b/tools/include/uapi/asm-generic/socket.h
@@ -145,6 +145,8 @@
#define SO_RCVPRIORITY 82
+#define SO_PASSRIGHTS 83
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v1 net-next 7/7] selftest: af_unix: Test SO_PASSRIGHTS.
2025-05-08 1:29 [PATCH v1 net-next 0/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
` (5 preceding siblings ...)
2025-05-08 1:29 ` [PATCH v1 net-next 6/7] af_unix: Introduce SO_PASSRIGHTS Kuniyuki Iwashima
@ 2025-05-08 1:29 ` Kuniyuki Iwashima
6 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-05-08 1:29 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Willem de Bruijn
Cc: Simon Horman, Christian Brauner, Kuniyuki Iwashima,
Kuniyuki Iwashima, netdev
scm_rights.c has various patterns of tests to exercise GC.
Let's add cases where SO_PASSRIGHTS is disabled.
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
.../selftests/net/af_unix/scm_rights.c | 84 ++++++++++++++++++-
1 file changed, 81 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/net/af_unix/scm_rights.c b/tools/testing/selftests/net/af_unix/scm_rights.c
index d66336256580..7589f690fe2f 100644
--- a/tools/testing/selftests/net/af_unix/scm_rights.c
+++ b/tools/testing/selftests/net/af_unix/scm_rights.c
@@ -23,6 +23,7 @@ FIXTURE_VARIANT(scm_rights)
int type;
int flags;
bool test_listener;
+ bool disabled;
};
FIXTURE_VARIANT_ADD(scm_rights, dgram)
@@ -31,6 +32,16 @@ FIXTURE_VARIANT_ADD(scm_rights, dgram)
.type = SOCK_DGRAM,
.flags = 0,
.test_listener = false,
+ .disabled = false,
+};
+
+FIXTURE_VARIANT_ADD(scm_rights, dgram_disabled)
+{
+ .name = "UNIX ",
+ .type = SOCK_DGRAM,
+ .flags = 0,
+ .test_listener = false,
+ .disabled = true,
};
FIXTURE_VARIANT_ADD(scm_rights, stream)
@@ -39,6 +50,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream)
.type = SOCK_STREAM,
.flags = 0,
.test_listener = false,
+ .disabled = false,
+};
+
+FIXTURE_VARIANT_ADD(scm_rights, stream_disabled)
+{
+ .name = "UNIX-STREAM ",
+ .type = SOCK_STREAM,
+ .flags = 0,
+ .test_listener = false,
+ .disabled = true,
};
FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
@@ -47,6 +68,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
.type = SOCK_STREAM,
.flags = MSG_OOB,
.test_listener = false,
+ .disabled = false,
+};
+
+FIXTURE_VARIANT_ADD(scm_rights, stream_oob_disabled)
+{
+ .name = "UNIX-STREAM ",
+ .type = SOCK_STREAM,
+ .flags = MSG_OOB,
+ .test_listener = false,
+ .disabled = true,
};
FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
@@ -55,6 +86,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
.type = SOCK_STREAM,
.flags = 0,
.test_listener = true,
+ .disabled = false,
+};
+
+FIXTURE_VARIANT_ADD(scm_rights, stream_listener_disabled)
+{
+ .name = "UNIX-STREAM ",
+ .type = SOCK_STREAM,
+ .flags = 0,
+ .test_listener = true,
+ .disabled = true,
};
FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
@@ -63,6 +104,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
.type = SOCK_STREAM,
.flags = MSG_OOB,
.test_listener = true,
+ .disabled = false,
+};
+
+FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob_disabled)
+{
+ .name = "UNIX-STREAM ",
+ .type = SOCK_STREAM,
+ .flags = MSG_OOB,
+ .test_listener = true,
+ .disabled = true,
};
static int count_sockets(struct __test_metadata *_metadata,
@@ -105,6 +156,9 @@ FIXTURE_SETUP(scm_rights)
ret = unshare(CLONE_NEWNET);
ASSERT_EQ(0, ret);
+ if (variant->disabled)
+ return;
+
ret = count_sockets(_metadata, variant);
ASSERT_EQ(0, ret);
}
@@ -113,6 +167,9 @@ FIXTURE_TEARDOWN(scm_rights)
{
int ret;
+ if (variant->disabled)
+ return;
+
sleep(1);
ret = count_sockets(_metadata, variant);
@@ -121,6 +178,7 @@ FIXTURE_TEARDOWN(scm_rights)
static void create_listeners(struct __test_metadata *_metadata,
FIXTURE_DATA(scm_rights) *self,
+ const FIXTURE_VARIANT(scm_rights) *variant,
int n)
{
struct sockaddr_un addr = {
@@ -140,6 +198,12 @@ static void create_listeners(struct __test_metadata *_metadata,
ret = listen(self->fd[i], -1);
ASSERT_EQ(0, ret);
+ if (variant->disabled) {
+ ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
+ &(int){0}, sizeof(int));
+ ASSERT_EQ(0, ret);
+ }
+
addrlen = sizeof(addr);
ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
ASSERT_EQ(0, ret);
@@ -164,6 +228,12 @@ static void create_socketpairs(struct __test_metadata *_metadata,
for (i = 0; i < n * 2; i += 2) {
ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
ASSERT_EQ(0, ret);
+
+ if (variant->disabled) {
+ ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
+ &(int){0}, sizeof(int));
+ ASSERT_EQ(0, ret);
+ }
}
}
@@ -175,7 +245,7 @@ static void __create_sockets(struct __test_metadata *_metadata,
ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
if (variant->test_listener)
- create_listeners(_metadata, self, n);
+ create_listeners(_metadata, self, variant, n);
else
create_socketpairs(_metadata, self, variant, n);
}
@@ -227,10 +297,18 @@ void __send_fd(struct __test_metadata *_metadata,
.msg_control = &cmsg,
.msg_controllen = CMSG_SPACE(sizeof(cmsg.fd)),
};
- int ret;
+ int ret, saved_errno;
+ errno = 0;
ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
- ASSERT_EQ(MSGLEN, ret);
+ saved_errno = errno;
+
+ if (variant->disabled) {
+ ASSERT_EQ(-1, ret);
+ ASSERT_EQ(-EPERM, -saved_errno);
+ } else {
+ ASSERT_EQ(MSGLEN, ret);
+ }
}
#define create_sockets(n) \
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread