Commit ab04570d authored by Tung Nguyen's avatar Tung Nguyen Committed by Greg Kroah-Hartman

tipc: fix race condition causing hung sendto

[ Upstream commit bfd07f3d ]

When sending multicast messages via blocking socket,
if sending link is congested (tsk->cong_link_cnt is set to 1),
the sending thread will be put into sleeping state. However,
tipc_sk_filter_rcv() is called under socket spin lock but
tipc_wait_for_cond() is not. So, there is no guarantee that
the setting of tsk->cong_link_cnt to 0 in tipc_sk_proto_rcv() in
CPU-1 will be perceived by CPU-0. If that is the case, the sending
thread in CPU-0 after being waken up, will continue to see
tsk->cong_link_cnt as 1 and put the sending thread into sleeping
state again. The sending thread will sleep forever.

CPU-0                                | CPU-1
tipc_wait_for_cond()                 |
{                                    |
 // condition_ = !tsk->cong_link_cnt |
 while ((rc_ = !(condition_))) {     |
  ...                                |
  release_sock(sk_);                 |
  wait_woken();                      |
                                     | if (!sock_owned_by_user(sk))
                                     |  tipc_sk_filter_rcv()
                                     |  {
                                     |   ...
                                     |   tipc_sk_proto_rcv()
                                     |   {
                                     |    ...
                                     |    tsk->cong_link_cnt--;
                                     |    ...
                                     |    sk->sk_write_space(sk);
                                     |    ...
                                     |   }
                                     |   ...
                                     |  }
  sched_annotate_sleep();            |
  lock_sock(sk_);                    |
  remove_wait_queue();               |
 }                                   |
}                                    |

This commit fixes it by adding memory barrier to tipc_sk_proto_rcv()
and tipc_wait_for_cond().
Acked-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarTung Nguyen <tung.q.nguyen@dektech.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5fdb551f
...@@ -377,11 +377,13 @@ static int tipc_sk_sock_err(struct socket *sock, long *timeout) ...@@ -377,11 +377,13 @@ static int tipc_sk_sock_err(struct socket *sock, long *timeout)
#define tipc_wait_for_cond(sock_, timeo_, condition_) \ #define tipc_wait_for_cond(sock_, timeo_, condition_) \
({ \ ({ \
DEFINE_WAIT_FUNC(wait_, woken_wake_function); \
struct sock *sk_; \ struct sock *sk_; \
int rc_; \ int rc_; \
\ \
while ((rc_ = !(condition_))) { \ while ((rc_ = !(condition_))) { \
DEFINE_WAIT_FUNC(wait_, woken_wake_function); \ /* coupled with smp_wmb() in tipc_sk_proto_rcv() */ \
smp_rmb(); \
sk_ = (sock_)->sk; \ sk_ = (sock_)->sk; \
rc_ = tipc_sk_sock_err((sock_), timeo_); \ rc_ = tipc_sk_sock_err((sock_), timeo_); \
if (rc_) \ if (rc_) \
...@@ -1961,6 +1963,8 @@ static void tipc_sk_proto_rcv(struct sock *sk, ...@@ -1961,6 +1963,8 @@ static void tipc_sk_proto_rcv(struct sock *sk,
return; return;
case SOCK_WAKEUP: case SOCK_WAKEUP:
tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0); tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0);
/* coupled with smp_rmb() in tipc_wait_for_cond() */
smp_wmb();
tsk->cong_link_cnt--; tsk->cong_link_cnt--;
wakeup = true; wakeup = true;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment