Fix kern/39769: race condition in TCP timers

When a TCP timer is disarmed (with callout_stop()) in the general case
callout_invoking() isn't checked, so the timer handler could be called run
when the current interrupt handler exits, athough the timer is disarmed.
This case cause bad things like TCPT_REXMT and TCPT_PERSIST being both pending,
causing a panic (see the PR for details).
Close the issue by aborting the handler if the timer is not callout_expired().
(the EXPIRED flag being cleared by callout_stop()).
This commit is contained in:
bouyer 2008-11-09 17:32:38 +00:00
parent 3ab32a0a5e
commit 71d4948e50
1 changed files with 23 additions and 3 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_timer.c,v 1.82 2008/10/10 10:21:05 ad Exp $ */ /* $NetBSD: tcp_timer.c,v 1.83 2008/11/09 17:32:38 bouyer Exp $ */
/* /*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -93,7 +93,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tcp_timer.c,v 1.82 2008/10/10 10:21:05 ad Exp $"); __KERNEL_RCSID(0, "$NetBSD: tcp_timer.c,v 1.83 2008/11/09 17:32:38 bouyer Exp $");
#include "opt_inet.h" #include "opt_inet.h"
#include "opt_tcp_debug.h" #include "opt_tcp_debug.h"
@ -212,6 +212,10 @@ tcp_delack(void *arg)
mutex_exit(softnet_lock); mutex_exit(softnet_lock);
return; return;
} }
if (!callout_expired(&tp->t_delack_ch)) {
mutex_exit(softnet_lock);
return;
}
tp->t_flags |= TF_ACKNOW; tp->t_flags |= TF_ACKNOW;
KERNEL_LOCK(1, NULL); KERNEL_LOCK(1, NULL);
@ -271,6 +275,10 @@ tcp_timer_rexmt(void *arg)
mutex_exit(softnet_lock); mutex_exit(softnet_lock);
return; return;
} }
if (!callout_expired(&tp->t_timer[TCPT_REXMT])) {
mutex_exit(softnet_lock);
return;
}
KERNEL_LOCK(1, NULL); KERNEL_LOCK(1, NULL);
if ((tp->t_flags & TF_PMTUD_PEND) && tp->t_inpcb && if ((tp->t_flags & TF_PMTUD_PEND) && tp->t_inpcb &&
@ -428,6 +436,10 @@ tcp_timer_persist(void *arg)
mutex_exit(softnet_lock); mutex_exit(softnet_lock);
return; return;
} }
if (!callout_expired(&tp->t_timer[TCPT_PERSIST])) {
mutex_exit(softnet_lock);
return;
}
KERNEL_LOCK(1, NULL); KERNEL_LOCK(1, NULL);
#ifdef TCP_DEBUG #ifdef TCP_DEBUG
@ -495,6 +507,10 @@ tcp_timer_keep(void *arg)
mutex_exit(softnet_lock); mutex_exit(softnet_lock);
return; return;
} }
if (!callout_expired(&tp->t_timer[TCPT_KEEP])) {
mutex_exit(softnet_lock);
return;
}
KERNEL_LOCK(1, NULL); KERNEL_LOCK(1, NULL);
@ -585,6 +601,10 @@ tcp_timer_2msl(void *arg)
mutex_exit(softnet_lock); mutex_exit(softnet_lock);
return; return;
} }
if (!callout_expired(&tp->t_timer[TCPT_2MSL])) {
mutex_exit(softnet_lock);
return;
}
/* /*
* 2 MSL timeout went off, clear the SACK scoreboard, reset * 2 MSL timeout went off, clear the SACK scoreboard, reset