diff options
author | Vlad Yasevich <vladislav.yasevich@hp.com> | 2009-01-22 14:53:23 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-22 14:53:23 -0800 |
commit | ae53b5bd77719fed58086c5be60ce4f22bffe1c6 (patch) | |
tree | b48da8033f59117512a5486a779b0853a255dc7b /net | |
parent | 759af00ebef858015eb68876ac1f383bcb6a1774 (diff) |
sctp: Fix another socket race during accept/peeloff
There is a race between sctp_rcv() and sctp_accept() where we
have moved the association from the listening socket to the
accepted socket, but sctp_rcv() processing cached the old
socket and continues to use it.
The easy solution is to check for the socket mismatch once we've
grabed the socket lock. If we hit a mis-match, that means
that were are currently holding the lock on the listening socket,
but the association is refrencing a newly accepted socket. We need
to drop the lock on the old socket and grab the lock on the new one.
A more proper solution might be to create accepted sockets when
the new association is established, similar to TCP. That would
eliminate the race for 1-to-1 style sockets, but it would still
existing for 1-to-many sockets where a user wished to peeloff an
association. For now, we'll live with this easy solution as
it addresses the problem.
Reported-by: Michal Hocko <mhocko@suse.cz>
Reported-by: Karsten Keil <kkeil@suse.de>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/sctp/input.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/net/sctp/input.c b/net/sctp/input.c index bf612d954d4..2e4a8646dbc 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -249,6 +249,19 @@ int sctp_rcv(struct sk_buff *skb) */ sctp_bh_lock_sock(sk); + if (sk != rcvr->sk) { + /* Our cached sk is different from the rcvr->sk. This is + * because migrate()/accept() may have moved the association + * to a new socket and released all the sockets. So now we + * are holding a lock on the old socket while the user may + * be doing something with the new socket. Switch our veiw + * of the current sk. + */ + sctp_bh_unlock_sock(sk); + sk = rcvr->sk; + sctp_bh_lock_sock(sk); + } + if (sock_owned_by_user(sk)) { SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_BACKLOG); sctp_add_backlog(sk, skb); |