Skip to content

Commit 3f84d57

Browse files
q2vendavem330
authored andcommitted
af_unix: Inherit sk_flags at connect().
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, unix_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 would not for SO_PASSRIGHTS; once an SCM_RIGHTS with a hung file descriptor was sent, it'd be game over. To avoid this, we will need to preserve SOCK_PASSRIGHTS even on embryo sockets. Commit aed6ece ("af_unix: Save listener for embryo socket.") 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, accept()ed sockets and clients. Therefore, we moved SOCK_PASSXXX into struct sock. Let’s inherit sk->sk_scm_recv_flags at connect() to avoid receiving SCM_RIGHTS on embryo sockets created from a parent with SO_PASSRIGHTS=0. Note that the parent socket is locked in connect() so we don't need READ_ONCE() for sk_scm_recv_flags. Now, we can remove !other->sk_socket check in unix_maybe_add_creds() to avoid slow SOCK_PASS{CRED,PIDFD} handling for embryo sockets created from a parent with SO_PASS{CRED,PIDFD}=0. Signed-off-by: Kuniyuki Iwashima <[email protected]> Reviewed-by: Willem de Bruijn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0e81cfd commit 3f84d57

File tree

1 file changed

+6
-6
lines changed

1 file changed

+6
-6
lines changed

net/unix/af_unix.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,10 +1626,12 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
16261626
/* The way is open! Fastly set all the necessary fields... */
16271627

16281628
sock_hold(sk);
1629-
unix_peer(newsk) = sk;
1630-
newsk->sk_state = TCP_ESTABLISHED;
1631-
newsk->sk_type = sk->sk_type;
1629+
unix_peer(newsk) = sk;
1630+
newsk->sk_state = TCP_ESTABLISHED;
1631+
newsk->sk_type = sk->sk_type;
1632+
newsk->sk_scm_recv_flags = other->sk_scm_recv_flags;
16321633
init_peercred(newsk);
1634+
16331635
newu = unix_sk(newsk);
16341636
newu->listener = other;
16351637
RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
@@ -1746,7 +1748,6 @@ static int unix_accept(struct socket *sock, struct socket *newsock,
17461748
unix_state_lock(tsk);
17471749
unix_update_edges(unix_sk(tsk));
17481750
newsock->state = SS_CONNECTED;
1749-
tsk->sk_scm_recv_flags = READ_ONCE(sk->sk_scm_recv_flags);
17501751
sock_graft(tsk, newsock);
17511752
unix_state_unlock(tsk);
17521753
return 0;
@@ -1878,8 +1879,7 @@ static void unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
18781879
if (UNIXCB(skb).pid)
18791880
return;
18801881

1881-
if (unix_may_passcred(sk) ||
1882-
!other->sk_socket || unix_may_passcred(other)) {
1882+
if (unix_may_passcred(sk) || unix_may_passcred(other)) {
18831883
UNIXCB(skb).pid = get_pid(task_tgid(current));
18841884
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
18851885
}

0 commit comments

Comments
 (0)