Author: hselasky
Date: Thu Mar 24 09:22:58 2016
New Revision: 297228
URL: https://svnweb.freebsd.org/changeset/base/297228

Log:
  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

Modified:
  stable/10/sys/netpfil/ipfw/ip_dn_io.c
  stable/10/sys/netpfil/ipfw/ip_dummynet.c
  stable/10/sys/netpfil/ipfw/ip_fw2.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/netpfil/ipfw/ip_dn_io.c
==============================================================================
--- stable/10/sys/netpfil/ipfw/ip_dn_io.c       Thu Mar 24 08:26:06 2016        
(r297227)
+++ stable/10/sys/netpfil/ipfw/ip_dn_io.c       Thu Mar 24 09:22:58 2016        
(r297228)
@@ -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();

Modified: stable/10/sys/netpfil/ipfw/ip_dummynet.c
==============================================================================
--- stable/10/sys/netpfil/ipfw/ip_dummynet.c    Thu Mar 24 08:26:06 2016        
(r297227)
+++ stable/10/sys/netpfil/ipfw/ip_dummynet.c    Thu Mar 24 09:22:58 2016        
(r297228)
@@ -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);
 

Modified: stable/10/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- stable/10/sys/netpfil/ipfw/ip_fw2.c Thu Mar 24 08:26:06 2016        
(r297227)
+++ stable/10/sys/netpfil/ipfw/ip_fw2.c Thu Mar 24 09:22:58 2016        
(r297228)
@@ -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);
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to