Karsten, Michal I think I found this cursed race. The problem is that nothing is guarding the asoc->base.sk reference. This reference changes during the accept()/peeloff() code paths and the only guard around it is the socket lock. But that lock is not taken when the reference is used for reading as it is done in sctp_rcv(). So, when we do sctp_bh_lock_sock(sk) in sctp_rcv(), we may be using a stale cached copy of the sk which might have changed during sctp_accept(). What this allows us to do is to have a user sending a packet on a socket at the same time we may be processing and incoming packet on the same socket. We just end up taking different locks which totally hoses us. -vlad
diff --git a/net/sctp/input.c b/net/sctp/input.c index bf612d9..2e4a864 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);