MFC r292254:

Properly drain callouts in the IPFW subsystem to avoid use after free
panics when unloading the dummynet and IPFW modules:

- The callout drain function can sleep and should not be called having
a non-sleepable lock locked. Remove locks around "ipfw_dyn_uninit(0)".

- Add a new "dn_gone" variable to prevent asynchronous restart of
dummynet callouts when unloading the dummynet kernel module.

- Call "dn_reschedule()" locked so that "dn_gone" can be set and
checked atomically with regard to starting a new callout.

PR:			208171
Requested by:		Franco Fichtner (opnsense.org)
Differential Revision:	https://reviews.freebsd.org/D3855
This commit is contained in:
Hans Petter Selasky 2016-03-24 09:22:58 +00:00
parent 9c89526448
commit e2e9c7067a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/stable/10/; revision=297228
3 changed files with 11 additions and 6 deletions

View File

@ -619,8 +619,8 @@ dummynet_task(void *context, int pending)
dn_drain_queue();
}
DN_BH_WUNLOCK();
dn_reschedule();
DN_BH_WUNLOCK();
if (q.head != NULL)
dummynet_send(q.head);
CURVNET_RESTORE();

View File

@ -74,6 +74,7 @@ struct schk_new_arg {
/*---- callout hooks. ----*/
static struct callout dn_timeout;
static int dn_gone;
static struct task dn_task;
static struct taskqueue *dn_tq = NULL;
@ -89,6 +90,8 @@ void
dn_reschedule(void)
{
if (dn_gone != 0)
return;
callout_reset_sbt(&dn_timeout, tick_sbt, 0, dummynet, NULL,
C_HARDCLOCK | C_DIRECT_EXEC);
}
@ -2175,9 +2178,11 @@ ip_dn_init(void)
static void
ip_dn_destroy(int last)
{
callout_drain(&dn_timeout);
DN_BH_WLOCK();
/* ensure no more callouts are started */
dn_gone = 1;
/* check for last */
if (last) {
ND("removing last instance\n");
ip_dn_ctl_ptr = NULL;
@ -2186,6 +2191,8 @@ ip_dn_destroy(int last)
dummynet_flush();
DN_BH_WUNLOCK();
callout_drain(&dn_timeout);
taskqueue_drain(dn_tq, &dn_task);
taskqueue_free(dn_tq);

View File

@ -2704,12 +2704,10 @@ vnet_ipfw_uninit(const void *unused)
V_ip_fw_ctl_ptr = NULL;
IPFW_UH_WLOCK(chain);
IPFW_UH_WUNLOCK(chain);
IPFW_UH_WLOCK(chain);
IPFW_WLOCK(chain);
ipfw_dyn_uninit(0); /* run the callout_drain */
IPFW_WUNLOCK(chain);
IPFW_UH_WLOCK(chain);
ipfw_destroy_tables(chain);
reap = NULL;
IPFW_WLOCK(chain);