From: P Litov <[EMAIL PROTECTED]>
This patch corrects an SMP system-specific race condition which allowed
TIPC to prematurely dereference the first sk_buff in a socket receive
queue that was changing from empty to non-empty state.
Signed-off-by: Allan Stephens <[EMAIL PROTECTED]>
Signed-off-by: Per Liden <[EMAIL PROTECTED]>
---
net/tipc/socket.c | 40 ++--
1 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 2a6a5a6..827f204 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -82,6 +82,18 @@ static int sockets_enabled = 0;
static atomic_t tipc_queue_size = ATOMIC_INIT(0);
+/*
+ * Notes on receive queue locking:
+ *
+ * 1) Routines called from an application thread that examine the socket
+ * receive queue must typically use skb_queue_empty() rather than
+ * skb_queue_len() to determine if the queue has a message in it; otherwise,
+ * a race condition can arise on SMP systems wherein skb_queue_len() indicates
+ * the presence of a message that is not yet on the queue. (This race could
+ * be avoided by taking the queue lock before examining the queue, but this
+ * would complicate the code and impact performance.)
+ */
+
/*
* sock_lock(): Lock a port/socket pair. lock_sock() can
* not be used here, since the same lock must protect ports
@@ -123,7 +135,7 @@ static u32 pollmask(struct socket *sock)
{
u32 mask;
- if ((skb_queue_len(&sock->sk->sk_receive_queue) != 0) ||
+ if (!skb_queue_empty(&sock->sk->sk_receive_queue) ||
(sock->state == SS_UNCONNECTED) ||
(sock->state == SS_DISCONNECTING))
mask = (POLLRDNORM | POLLIN);
@@ -809,7 +821,7 @@ static int recv_msg(struct kiocb *iocb,
struct tipc_sock *tsock = tipc_sk(sock->sk);
struct sk_buff *buf;
struct tipc_msg *msg;
- unsigned int q_len;
+ int q_empty;
unsigned int sz;
u32 err;
int res;
@@ -828,7 +840,7 @@ static int recv_msg(struct kiocb *iocb,
if (unlikely(sock->state == SS_UNCONNECTED))
return -ENOTCONN;
if (unlikely((sock->state == SS_DISCONNECTING) &&
-(skb_queue_len(&sock->sk->sk_receive_queue) == 0)))
+(skb_queue_empty(&sock->sk->sk_receive_queue
return -ENOTCONN;
}
@@ -838,7 +850,7 @@ static int recv_msg(struct kiocb *iocb,
return -ERESTARTSYS;
restart:
- if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) &&
+ if (unlikely(skb_queue_empty(&sock->sk->sk_receive_queue) &&
(flags & MSG_DONTWAIT))) {
res = -EWOULDBLOCK;
goto exit;
@@ -846,7 +858,7 @@ restart:
if ((res = wait_event_interruptible(
*sock->sk->sk_sleep,
- ((q_len = skb_queue_len(&sock->sk->sk_receive_queue)) ||
+ (!(q_empty = skb_queue_empty(&sock->sk->sk_receive_queue)) ||
(sock->state == SS_DISCONNECTING))) )) {
goto exit;
}
@@ -854,7 +866,7 @@ restart:
/* Catch attempt to receive on an already terminated connection */
/* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */
- if (!q_len) {
+ if (q_empty) {
res = -ENOTCONN;
goto exit;
}
@@ -941,7 +953,7 @@ static int recv_stream(struct kiocb *ioc
struct tipc_sock *tsock = tipc_sk(sock->sk);
struct sk_buff *buf;
struct tipc_msg *msg;
- unsigned int q_len;
+ int q_empty;
unsigned int sz;
int sz_to_copy;
int sz_copied = 0;
@@ -962,7 +974,7 @@ static int recv_stream(struct kiocb *ioc
return -EINVAL;
if (unlikely(sock->state == SS_DISCONNECTING)) {
- if (skb_queue_len(&sock->sk->sk_receive_queue) == 0)
+ if (skb_queue_empty(&sock->sk->sk_receive_queue))
return -ENOTCONN;
} else if (unlikely(sock->state != SS_CONNECTED))
return -ENOTCONN;
@@ -973,7 +985,7 @@ static int recv_stream(struct kiocb *ioc
return -ERESTARTSYS;
restart:
- if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) &&
+ if (unlikely(skb_queue_empty(&sock->sk->sk_receive_queue) &&
(flags & MSG_DONTWAIT))) {
res = -EWOULDBLOCK;
goto exit;
@@ -981,7 +993,7 @@ restart:
if ((res = wait_event_interruptible(
*sock->sk->sk_sleep,
- ((q_len = skb_queue_len(&sock->sk->sk_receive_queue)) ||
+ (!(q_empty = skb_queue_empty(&sock->sk->sk_receive_queue)) ||
(sock->state == SS_DISCONNECTING))) )) {
goto exit;
}
@@ -989,7 +1001,7 @@ restart:
/* Catch attempt to receive on an already terminated connection