mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 12:21:22 -05:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user