Merge branch 'af_unix-prepare-for-skb-drop-reason'

Kuniyuki Iwashima says:

====================
af_unix: Prepare for skb drop reason.

This is a prep series and cleans up error paths in the following
functions

  * unix_stream_connect()
  * unix_stream_sendmsg()
  * unix_dgram_sendmsg()

to make it easy to add skb drop reason for AF_UNIX, which seems to
have a potential user.

https://lore.kernel.org/netdev/CAAf2ycmZHti95WaBR3s+L5Epm1q7sXmvZ-EqCK=-oZj=45tOwQ@mail.gmail.com/

v1: https://lore.kernel.org/netdev/20241206052607.1197-1-kuniyu@amazon.com/
====================

Link: https://patch.msgid.link/20241213110850.25453-1-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2024-12-17 12:08:30 +01:00

View File

@@ -286,14 +286,9 @@ static inline bool unix_secdata_eq(struct scm_cookie *scm, struct sk_buff *skb)
}
#endif /* CONFIG_SECURITY_NETWORK */
static inline int unix_our_peer(struct sock *sk, struct sock *osk)
{
return unix_peer(osk) == sk;
}
static inline int unix_may_send(struct sock *sk, struct sock *osk)
{
return unix_peer(osk) == NULL || unix_our_peer(sk, osk);
return !unix_peer(osk) || unix_peer(osk) == sk;
}
static inline int unix_recvq_full_lockless(const struct sock *sk)
@@ -1563,32 +1558,30 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
/* First of all allocate resources.
If we will make it after state is locked,
we will have to recheck all again in any case.
* If we will make it after state is locked,
* we will have to recheck all again in any case.
*/
/* create new sock for complete connection */
newsk = unix_create1(net, NULL, 0, sock->type);
if (IS_ERR(newsk)) {
err = PTR_ERR(newsk);
newsk = NULL;
goto out;
}
err = -ENOMEM;
/* Allocate skb for sending to listening sock */
skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
if (skb == NULL)
goto out;
if (!skb) {
err = -ENOMEM;
goto out_free_sk;
}
restart:
/* Find listening sock. */
other = unix_find_other(net, sunaddr, addr_len, sk->sk_type);
if (IS_ERR(other)) {
err = PTR_ERR(other);
other = NULL;
goto out;
goto out_free_skb;
}
unix_state_lock(other);
@@ -1600,23 +1593,25 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
goto restart;
}
err = -ECONNREFUSED;
if (other->sk_state != TCP_LISTEN)
goto out_unlock;
if (other->sk_shutdown & RCV_SHUTDOWN)
if (other->sk_state != TCP_LISTEN ||
other->sk_shutdown & RCV_SHUTDOWN) {
err = -ECONNREFUSED;
goto out_unlock;
}
if (unix_recvq_full_lockless(other)) {
err = -EAGAIN;
if (!timeo)
if (!timeo) {
err = -EAGAIN;
goto out_unlock;
}
timeo = unix_wait_for_peer(other, timeo);
sock_put(other);
err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
sock_put(other);
goto out_free_skb;
goto restart;
}
@@ -1701,15 +1696,13 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
return 0;
out_unlock:
if (other)
unix_state_unlock(other);
out:
unix_state_unlock(other);
sock_put(other);
out_free_skb:
kfree_skb(skb);
if (newsk)
unix_release_sock(newsk, 0);
if (other)
sock_put(other);
out_free_sk:
unix_release_sock(newsk, 0);
out:
return err;
}
@@ -1964,7 +1957,6 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{
DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, msg->msg_name);
struct sock *sk = sock->sk, *other = NULL;
struct unix_sock *u = unix_sk(sk);
struct scm_cookie scm;
@@ -1980,12 +1972,13 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
wait_for_unix_gc(scm.fp);
err = -EOPNOTSUPP;
if (msg->msg_flags&MSG_OOB)
if (msg->msg_flags & MSG_OOB) {
err = -EOPNOTSUPP;
goto out;
}
if (msg->msg_namelen) {
err = unix_validate_addr(sunaddr, msg->msg_namelen);
err = unix_validate_addr(msg->msg_name, msg->msg_namelen);
if (err)
goto out;
@@ -1995,12 +1988,6 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
NULL);
if (err)
goto out;
} else {
sunaddr = NULL;
err = -ENOTCONN;
other = unix_peer_get(sk);
if (!other)
goto out;
}
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
@@ -2011,9 +1998,10 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
goto out;
}
err = -EMSGSIZE;
if (len > READ_ONCE(sk->sk_sndbuf) - 32)
if (len > READ_ONCE(sk->sk_sndbuf) - 32) {
err = -EMSGSIZE;
goto out;
}
if (len > SKB_MAX_ALLOC) {
data_len = min_t(size_t,
@@ -2027,7 +2015,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
skb = sock_alloc_send_pskb(sk, len - data_len, data_len,
msg->msg_flags & MSG_DONTWAIT, &err,
PAGE_ALLOC_COSTLY_ORDER);
if (skb == NULL)
if (!skb)
goto out;
err = unix_scm_to_skb(&scm, skb, true);
@@ -2043,17 +2031,18 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
restart:
if (!other) {
err = -ECONNRESET;
if (sunaddr == NULL)
goto out_free;
other = unix_find_other(sock_net(sk), sunaddr, msg->msg_namelen,
sk->sk_type);
if (msg->msg_namelen) {
lookup:
other = unix_find_other(sock_net(sk), msg->msg_name,
msg->msg_namelen, sk->sk_type);
if (IS_ERR(other)) {
err = PTR_ERR(other);
other = NULL;
goto out_free;
}
} else {
other = unix_peer_get(sk);
if (!other) {
err = -ENOTCONN;
goto out_free;
}
}
@@ -2061,36 +2050,37 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (sk_filter(other, skb) < 0) {
/* Toss the packet but do not return any error to the sender */
err = len;
goto out_free;
goto out_sock_put;
}
restart:
sk_locked = 0;
unix_state_lock(other);
restart_locked:
err = -EPERM;
if (!unix_may_send(sk, other))
if (!unix_may_send(sk, other)) {
err = -EPERM;
goto out_unlock;
}
if (unlikely(sock_flag(other, SOCK_DEAD))) {
/*
* Check with 1003.1g - what should
* datagram error
*/
/* Check with 1003.1g - what should datagram error */
unix_state_unlock(other);
sock_put(other);
if (!sk_locked)
unix_state_lock(sk);
err = 0;
if (sk->sk_type == SOCK_SEQPACKET) {
/* We are here only when racing with unix_release_sock()
* is clearing @other. Never change state to TCP_CLOSE
* unlike SOCK_DGRAM wants.
*/
unix_state_unlock(sk);
err = -EPIPE;
} else if (unix_peer(sk) == other) {
goto out_sock_put;
}
if (!sk_locked)
unix_state_lock(sk);
if (unix_peer(sk) == other) {
unix_peer(sk) = NULL;
unix_dgram_peer_wake_disconnect_wakeup(sk, other);
@@ -2100,19 +2090,23 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
unix_dgram_disconnected(sk, other);
sock_put(other);
err = -ECONNREFUSED;
} else {
unix_state_unlock(sk);
goto out_sock_put;
}
other = NULL;
if (err)
goto out_free;
goto restart;
unix_state_unlock(sk);
if (!msg->msg_namelen) {
err = -ECONNRESET;
goto out_sock_put;
}
goto lookup;
}
err = -EPIPE;
if (other->sk_shutdown & RCV_SHUTDOWN)
if (other->sk_shutdown & RCV_SHUTDOWN) {
err = -EPIPE;
goto out_unlock;
}
if (sk->sk_type != SOCK_SEQPACKET) {
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
@@ -2132,7 +2126,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out_free;
goto out_sock_put;
goto restart;
}
@@ -2173,11 +2167,11 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (sk_locked)
unix_state_unlock(sk);
unix_state_unlock(other);
out_sock_put:
sock_put(other);
out_free:
kfree_skb(skb);
out:
if (other)
sock_put(other);
scm_destroy(&scm);
return err;
}
@@ -2256,8 +2250,8 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
wait_for_unix_gc(scm.fp);
err = -EOPNOTSUPP;
if (msg->msg_flags & MSG_OOB) {
err = -EOPNOTSUPP;
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
if (len)
len--;
@@ -2270,14 +2264,20 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
err = READ_ONCE(sk->sk_state) == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP;
goto out_err;
} else {
err = -ENOTCONN;
other = unix_peer(sk);
if (!other)
if (!other) {
err = -ENOTCONN;
goto out_err;
}
}
if (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN)
goto pipe_err;
if (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) {
if (!(msg->msg_flags & MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0);
err = -EPIPE;
goto out_err;
}
while (sent < len) {
size = len - sent;
@@ -2306,20 +2306,18 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
/* Only send the fds in the first buffer */
err = unix_scm_to_skb(&scm, skb, !fds_sent);
if (err < 0) {
kfree_skb(skb);
goto out_err;
}
if (err < 0)
goto out_free;
fds_sent = true;
if (unlikely(msg->msg_flags & MSG_SPLICE_PAGES)) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
err = skb_splice_from_iter(skb, &msg->msg_iter, size,
sk->sk_allocation);
if (err < 0) {
kfree_skb(skb);
goto out_err;
}
if (err < 0)
goto out_free;
size = err;
refcount_add(size, &sk->sk_wmem_alloc);
} else {
@@ -2327,17 +2325,15 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
skb->data_len = data_len;
skb->len = size;
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
if (err) {
kfree_skb(skb);
goto out_err;
}
if (err)
goto out_free;
}
unix_state_lock(other);
if (sock_flag(other, SOCK_DEAD) ||
(other->sk_shutdown & RCV_SHUTDOWN))
goto pipe_err_free;
goto out_pipe;
maybe_add_creds(skb, sock, other);
scm_stat_add(other, skb);
@@ -2360,13 +2356,13 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
return sent;
pipe_err_free:
out_pipe:
unix_state_unlock(other);
kfree_skb(skb);
pipe_err:
if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
if (!sent && !(msg->msg_flags & MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0);
err = -EPIPE;
out_free:
kfree_skb(skb);
out_err:
scm_destroy(&scm);
return sent ? : err;