Commit 963b7895 authored by Paolo Abeni's avatar Paolo Abeni
Browse files

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: default avatarPaolo Abeni <pabeni@redhat.com>
parents a14a4290 bf61ffeb
Loading
Loading
Loading
Loading
+96 −100
Original line number Diff line number Diff line
@@ -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;
	}

	if (other->sk_state != TCP_LISTEN ||
	    other->sk_shutdown & RCV_SHUTDOWN) {
		err = -ECONNREFUSED;
	if (other->sk_state != TCP_LISTEN)
		goto out_unlock;
	if (other->sk_shutdown & RCV_SHUTDOWN)
		goto out_unlock;
	}

	if (unix_recvq_full_lockless(other)) {
		if (!timeo) {
			err = -EAGAIN;
		if (!timeo)
			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:
	sock_put(other);
out_free_skb:
	kfree_skb(skb);
	if (newsk)
out_free_sk:
	unix_release_sock(newsk, 0);
	if (other)
		sock_put(other);
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);

	if (msg->msg_flags & MSG_OOB) {
		err = -EOPNOTSUPP;
	if (msg->msg_flags&MSG_OOB)
		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;
	}

	if (len > READ_ONCE(sk->sk_sndbuf) - 32) {
		err = -EMSGSIZE;
	if (len > READ_ONCE(sk->sk_sndbuf) - 32)
		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:

	if (!unix_may_send(sk, other)) {
		err = -EPERM;
	if (!unix_may_send(sk, other))
		goto out_unlock;
	}

	if (unlikely(sock_flag(other, SOCK_DEAD))) {
		/*
		 *	Check with 1003.1g - what should
		 *	datagram error
		 */
		unix_state_unlock(other);
		sock_put(other);
		/* Check with 1003.1g - what should datagram error */

		if (!sk_locked)
			unix_state_lock(sk);
		unix_state_unlock(other);

		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 {
			goto out_sock_put;
		}

		unix_state_unlock(sk);

		if (!msg->msg_namelen) {
			err = -ECONNRESET;
			goto out_sock_put;
		}

		other = NULL;
		if (err)
			goto out_free;
		goto restart;
		goto lookup;
	}

	if (other->sk_shutdown & RCV_SHUTDOWN) {
		err = -EPIPE;
	if (other->sk_shutdown & RCV_SHUTDOWN)
		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) {
		if (!(msg->msg_flags & MSG_NOSIGNAL))
			send_sig(SIGPIPE, current, 0);

	if (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN)
		goto pipe_err;
		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;