inet: fix race in reqsk_queue_unlink()

reqsk_timer_handler() tests if icsk_accept_queue.listen_opt
is NULL at its beginning.

By the time it calls inet_csk_reqsk_queue_drop() and
reqsk_queue_unlink(), listener might have been closed and
inet_csk_listen_stop() had called reqsk_queue_yank_acceptq()
which sets icsk_accept_queue.listen_opt to NULL

We therefore need to correctly check listen_opt being NULL
after holding syn_wait_lock for proper synchronization.

Fixes: fa76ce7328 ("inet: get rid of central tcp/dccp listener timer")
Fixes: b357a364c5 ("inet: fix possible panic in reqsk_queue_unlink()")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2015-10-01 05:39:26 -07:00 committed by David S. Miller
parent 8695a144da
commit 2306c704ce
1 changed files with 10 additions and 9 deletions

View File

@ -577,21 +577,22 @@ EXPORT_SYMBOL(inet_rtx_syn_ack);
static bool reqsk_queue_unlink(struct request_sock_queue *queue, static bool reqsk_queue_unlink(struct request_sock_queue *queue,
struct request_sock *req) struct request_sock *req)
{ {
struct listen_sock *lopt = queue->listen_opt;
struct request_sock **prev; struct request_sock **prev;
struct listen_sock *lopt;
bool found = false; bool found = false;
spin_lock(&queue->syn_wait_lock); spin_lock(&queue->syn_wait_lock);
lopt = queue->listen_opt;
for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; if (lopt) {
prev = &(*prev)->dl_next) { for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL;
if (*prev == req) { prev = &(*prev)->dl_next) {
*prev = req->dl_next; if (*prev == req) {
found = true; *prev = req->dl_next;
break; found = true;
break;
}
} }
} }
spin_unlock(&queue->syn_wait_lock); spin_unlock(&queue->syn_wait_lock);
if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer)) if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer))
reqsk_put(req); reqsk_put(req);