@@ -13,6 +13,7 @@
FN(SOCKET_RCV_SHUTDOWN) \
FN(UNIX_DISCONNECT) \
FN(UNIX_INFLIGHT_FD_LIMIT) \
+ FN(UNIX_NO_PERM) \
FN(UNIX_SKIP_OOB) \
FN(PKT_TOO_SMALL) \
FN(TCP_CSUM) \
@@ -164,6 +165,11 @@ enum skb_drop_reason {
* are passed via SCM_RIGHTS but not yet received, reaching RLIMIT_NOFILE.
*/
SKB_DROP_REASON_UNIX_INFLIGHT_FD_LIMIT,
+ /**
+ * @SKB_DROP_REASON_UNIX_NO_PERM: the peer socket is already connect()ed
+ * to another SOCK_DGRAM/SOCK_SEQPACKET socket.
+ */
+ SKB_DROP_REASON_UNIX_NO_PERM,
/**
* @SKB_DROP_REASON_UNIX_SKIP_OOB: Out-Of-Band data is skipped by
* recv() without MSG_OOB so dropped.
@@ -1991,6 +1991,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
{
struct sock *sk = sock->sk, *other = NULL;
struct unix_sock *u = unix_sk(sk);
+ enum skb_drop_reason reason;
struct scm_cookie scm;
struct sk_buff *skb;
int data_len = 0;
@@ -2051,15 +2052,19 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
goto out;
err = unix_scm_to_skb(&scm, skb, true);
- if (err < 0)
+ if (err < 0) {
+ reason = unix_scm_err_to_reason(err);
goto out_free;
+ }
skb_put(skb, len - data_len);
skb->data_len = data_len;
skb->len = len;
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len);
- if (err)
+ if (err) {
+ reason = SKB_DROP_REASON_SKB_UCOPY_FAULT;
goto out_free;
+ }
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
@@ -2069,12 +2074,14 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
msg->msg_namelen, sk->sk_type);
if (IS_ERR(other)) {
err = PTR_ERR(other);
+ reason = SKB_DROP_REASON_NO_SOCKET;
goto out_free;
}
} else {
other = unix_peer_get(sk);
if (!other) {
err = -ENOTCONN;
+ reason = SKB_DROP_REASON_NO_SOCKET;
goto out_free;
}
}
@@ -2082,6 +2089,7 @@ 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;
+ reason = SKB_DROP_REASON_SOCKET_FILTER;
goto out_sock_put;
}
@@ -2092,6 +2100,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (!unix_may_send(sk, other)) {
err = -EPERM;
+ reason = SKB_DROP_REASON_UNIX_NO_PERM;
goto out_unlock;
}
@@ -2106,6 +2115,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
* unlike SOCK_DGRAM wants.
*/
err = -EPIPE;
+ reason = SKB_DROP_REASON_SOCKET_CLOSE;
goto out_sock_put;
}
@@ -2122,6 +2132,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
unix_dgram_disconnected(sk, other);
sock_put(other);
err = -ECONNREFUSED;
+ reason = SKB_DROP_REASON_SOCKET_CLOSE;
goto out_sock_put;
}
@@ -2129,6 +2140,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (!msg->msg_namelen) {
err = -ECONNRESET;
+ reason = SKB_DROP_REASON_SOCKET_CLOSE;
goto out_sock_put;
}
@@ -2137,13 +2149,16 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (other->sk_shutdown & RCV_SHUTDOWN) {
err = -EPIPE;
+ reason = SKB_DROP_REASON_SOCKET_RCV_SHUTDOWN;
goto out_unlock;
}
if (sk->sk_type != SOCK_SEQPACKET) {
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
- if (err)
+ if (err) {
+ reason = SKB_DROP_REASON_SECURITY_HOOK;
goto out_unlock;
+ }
}
/* other == sk && unix_peer(other) != sk if
@@ -2157,8 +2172,10 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
timeo = unix_wait_for_peer(other, timeo);
err = sock_intr_errno(timeo);
- if (signal_pending(current))
+ if (signal_pending(current)) {
+ reason = SKB_DROP_REASON_SOCKET_RCVBUFF;
goto out_sock_put;
+ }
goto restart;
}
@@ -2171,6 +2188,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (unix_peer(sk) != other ||
unix_dgram_peer_wake_me(sk, other)) {
err = -EAGAIN;
+ reason = SKB_DROP_REASON_SOCKET_RCVBUFF;
sk_locked = 1;
goto out_unlock;
}
@@ -2202,7 +2220,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
out_sock_put:
sock_put(other);
out_free:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
out:
scm_destroy(&scm);
return err;