lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <1409711953-30543-1-git-send-email-willemb@google.com>
Date:	Tue,  2 Sep 2014 22:39:13 -0400
From:	Willem de Bruijn <willemb@...gle.com>
To:	netdev@...r.kernel.org
Cc:	davem@...emloft.net, hannes@...hat.com, eric.dumazet@...il.com,
	Willem de Bruijn <willemb@...gle.com>
Subject: [PATCH rfc] sock: return error when sk_error_queue is not empty

[rfc related to http://patchwork.ozlabs.org/patch/384606/]

Sockets can have two types of errors: those set in sk->sk_err and
those queued onto sk->sk_error_queue. When either is non-zero,
regular socket syscall processing must aborted and the error handled.

Ensure consistent behavior by introducing two new helper functions:

  sock_has_error:    test for both types of errors
  sock_peek_err_skb: extract the first errno from sk_error_queue

and update the existing codepaths to use this. In particular, change
sock_error to call sock_peek_err_skb.

Previously, errnos of queued errors would be mirrored onto sk->sk_err
in some cases to return them. Now that the queue is checked directly,
remove this assignment.

Signed-off-by: Willem de Bruijn <willemb@...gle.com>
---
 include/net/sock.h           | 18 ++++++++++++++++++
 net/bluetooth/af_bluetooth.c |  2 +-
 net/core/datagram.c          |  2 +-
 net/core/skbuff.c            | 21 +++++++++++++++------
 net/core/sock.c              |  2 +-
 net/core/stream.c            |  2 +-
 net/ipv4/tcp.c               | 17 ++++++++---------
 net/iucv/af_iucv.c           |  2 +-
 net/nfc/llcp_sock.c          |  2 +-
 net/rxrpc/ar-output.c        |  2 +-
 net/sctp/socket.c            |  2 +-
 net/unix/af_unix.c           |  2 +-
 12 files changed, 50 insertions(+), 24 deletions(-)

diff --git a/include/net/sock.h b/include/net/sock.h
index 3fde613..c27e2cd 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2041,8 +2041,22 @@ void sk_stop_timer(struct sock *sk, struct timer_list *timer);
 int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
 
 int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb);
+int sock_dequeue_and_peek_err_skb(struct sock *sk, struct sk_buff **skb);
 struct sk_buff *sock_dequeue_err_skb(struct sock *sk);
 
+static inline bool sock_has_error(struct sock *sk)
+{
+	return sk->sk_err || !skb_queue_empty(&sk->sk_error_queue);
+}
+
+static inline int sock_peek_err_skb(struct sock *sk)
+{
+	if (unlikely(!skb_queue_empty(&sk->sk_error_queue)))
+		return sock_dequeue_and_peek_err_skb(sk, NULL);
+
+	return 0;
+}
+
 /*
  *	Recover an error report and clear atomically
  */
@@ -2050,6 +2064,10 @@ struct sk_buff *sock_dequeue_err_skb(struct sock *sk);
 static inline int sock_error(struct sock *sk)
 {
 	int err;
+
+	err = sock_peek_err_skb(sk);
+	if (err)
+		return err;
 	if (likely(!sk->sk_err))
 		return 0;
 	err = xchg(&sk->sk_err, 0);
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 4dca029..345dbc7 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -415,7 +415,7 @@ unsigned int bt_sock_poll(struct file *file, struct socket *sock,
 	if (sk->sk_state == BT_LISTEN)
 		return bt_accept_poll(sk);
 
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 488dd1a..2d585e9 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -851,7 +851,7 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
 	mask = 0;
 
 	/* exceptional events? */
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 53ce536..170a076 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3491,20 +3491,29 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(sock_queue_err_skb);
 
-struct sk_buff *sock_dequeue_err_skb(struct sock *sk)
+int sock_dequeue_and_peek_err_skb(struct sock *sk, struct sk_buff **skb)
 {
 	struct sk_buff_head *q = &sk->sk_error_queue;
-	struct sk_buff *skb, *skb_next;
+	struct sk_buff *skb_next;
 	int err = 0;
 
 	spin_lock_bh(&q->lock);
-	skb = __skb_dequeue(q);
-	if (skb && (skb_next = skb_peek(q)))
+	if (skb)
+		*skb = __skb_dequeue(q);
+	skb_next = skb_peek(q);
+	if (skb_next)
 		err = SKB_EXT_ERR(skb_next)->ee.ee_errno;
 	spin_unlock_bh(&q->lock);
 
-	sk->sk_err = err;
-	if (err)
+	return err;
+}
+EXPORT_SYMBOL(sock_dequeue_and_peek_err_skb);
+
+struct sk_buff *sock_dequeue_err_skb(struct sock *sk)
+{
+	struct sk_buff *skb;
+
+	if (sock_dequeue_and_peek_err_skb(sk, &skb))
 		sk->sk_error_report(sk);
 
 	return skb;
diff --git a/net/core/sock.c b/net/core/sock.c
index f1a638e..b5a7baa 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1739,7 +1739,7 @@ static long sock_wait_for_wmem(struct sock *sk, long timeo)
 			break;
 		if (sk->sk_shutdown & SEND_SHUTDOWN)
 			break;
-		if (sk->sk_err)
+		if (sock_has_error(sk))
 			break;
 		timeo = schedule_timeout(timeo);
 	}
diff --git a/net/core/stream.c b/net/core/stream.c
index 301c05f..1554648 100644
--- a/net/core/stream.c
+++ b/net/core/stream.c
@@ -129,7 +129,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
 
 		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
 
-		if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+		if (sock_has_error(sk) || (sk->sk_shutdown & SEND_SHUTDOWN))
 			goto do_error;
 		if (!*timeo_p)
 			goto do_nonblock;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 541f26a..4723ff9 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -534,7 +534,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
 	}
 	/* This barrier is coupled with smp_wmb() in tcp_reset() */
 	smp_rmb();
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR;
 
 	return mask;
@@ -757,10 +757,9 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 				break;
 			if (sock_flag(sk, SOCK_DONE))
 				break;
-			if (sk->sk_err) {
-				ret = sock_error(sk);
+			ret = sock_error(sk);
+			if (ret)
 				break;
-			}
 			if (sk->sk_shutdown & RCV_SHUTDOWN)
 				break;
 			if (sk->sk_state == TCP_CLOSE) {
@@ -791,7 +790,7 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 		release_sock(sk);
 		lock_sock(sk);
 
-		if (sk->sk_err || sk->sk_state == TCP_CLOSE ||
+		if (sock_has_error(sk) || sk->sk_state == TCP_CLOSE ||
 		    (sk->sk_shutdown & RCV_SHUTDOWN) ||
 		    signal_pending(current))
 			break;
@@ -914,7 +913,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
 	copied = 0;
 
 	err = -EPIPE;
-	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+	if (sock_has_error(sk) || (sk->sk_shutdown & SEND_SHUTDOWN))
 		goto out_err;
 
 	while (size > 0) {
@@ -1142,7 +1141,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 	copied = 0;
 
 	err = -EPIPE;
-	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+	if (sock_has_error(sk) || (sk->sk_shutdown & SEND_SHUTDOWN))
 		goto out_err;
 
 	sg = !!(sk->sk_route_caps & NETIF_F_SG);
@@ -1739,7 +1738,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 			break;
 
 		if (copied) {
-			if (sk->sk_err ||
+			if (sock_has_error(sk) ||
 			    sk->sk_state == TCP_CLOSE ||
 			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
 			    !timeo ||
@@ -1749,7 +1748,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 			if (sock_flag(sk, SOCK_DONE))
 				break;
 
-			if (sk->sk_err) {
+			if (sock_has_error(sk)) {
 				copied = sock_error(sk);
 				break;
 			}
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index a089b6b..5ef473c 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1465,7 +1465,7 @@ unsigned int iucv_sock_poll(struct file *file, struct socket *sock,
 	if (sk->sk_state == IUCV_LISTEN)
 		return iucv_accept_poll(sk);
 
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 51f077a..06e0411 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -553,7 +553,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
 	if (sk->sk_state == LLCP_LISTEN)
 		return llcp_accept_poll(sk);
 
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c
index 0b4b9a7..d663ed8 100644
--- a/net/rxrpc/ar-output.c
+++ b/net/rxrpc/ar-output.c
@@ -544,7 +544,7 @@ static int rxrpc_send_data(struct kiocb *iocb,
 	/* this should be in poll */
 	clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
 
-	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+	if (sock_has_error(sk) || (sk->sk_shutdown & SEND_SHUTDOWN))
 		return -EPIPE;
 
 	iov = msg->msg_iov;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index eb71d49..6609f17 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -6437,7 +6437,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
 	mask = 0;
 
 	/* Is there any exceptional events?  */
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 	if (sk->sk_shutdown & RCV_SHUTDOWN)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e968843..e9957fd 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2220,7 +2220,7 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
 	mask = 0;
 
 	/* exceptional events? */
-	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+	if (sock_has_error(sk))
 		mask |= POLLERR |
 			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
 
-- 
2.1.0.rc2.206.gedb03e5

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ