]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
pullup:
authorAndreas Gustafsson <source@isc.org>
Fri, 29 Dec 2000 19:16:36 +0000 (19:16 +0000)
committerAndreas Gustafsson <source@isc.org>
Fri, 29 Dec 2000 19:16:36 +0000 (19:16 +0000)
 642.   [bug]           Break the exit_check() race in the zone module.
                        [RT #598]
+ fix lock heirachy in zone_shutdown()

CHANGES
lib/dns/zone.c

diff --git a/CHANGES b/CHANGES
index 294f008e4ebca95ddbb64cdd02a8413094b02ec3..0906c3fa64856012ae3c25bda0e451a65a336064 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,7 @@
 
+ 642.  [bug]           Break the exit_check() race in the zone module.
+                       [RT #598]
+
        --- 9.1.0b2 released ---
 
  641.  [bug]           $GENERATE caused a uninitialized link to be used.
index cfab686eff79b2b7513eda85091856cb0880d5ab..38c0374d805349ac0a6260318b2a43668df05853 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.c,v 1.283 2000/12/28 01:29:06 marka Exp $ */
+/* $Id: zone.c,v 1.283.2.1 2000/12/29 19:13:56 gson Exp $ */
 
 #include <config.h>
 
@@ -247,6 +247,7 @@ struct dns_zone {
 #define DNS_ZONEFLG_NOREFRESH  0x00010000U
 #define DNS_ZONEFLG_DIALNOTIFY 0x00020000U
 #define DNS_ZONEFLG_DIALREFRESH        0x00040000U
+#define DNS_ZONEFLG_SHUTDOWN   0x00080000U
 
 #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0)
 
@@ -1253,17 +1254,21 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
        return (result);
 }
 
-static void
+static isc_boolean_t
 exit_check(dns_zone_t *zone) {
-       if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) &&
+
+       REQUIRE(LOCKED_ZONE(zone));
+
+       if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SHUTDOWN) &&
            zone->irefs == 0)
        {
                /*
-                * DNS_ZONEFLG_EXITING can only be set if erefs == 0.
+                * DNS_ZONEFLG_SHUTDOWN can only be set if erefs == 0.
                 */
                INSIST(zone->erefs == 0);
-               zone_free(zone);
+               return (ISC_TRUE);
        }
+       return (ISC_FALSE);
 }
 
 static isc_result_t
@@ -1520,6 +1525,7 @@ zone_idetach(dns_zone_t **zonep) {
 void
 dns_zone_idetach(dns_zone_t **zonep) {
        dns_zone_t *zone;
+       isc_boolean_t free_needed;
 
        REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
        zone = *zonep;
@@ -1530,8 +1536,10 @@ dns_zone_idetach(dns_zone_t **zonep) {
        zone->irefs--;
        zone_log(zone, "dns_zone_idetach", ISC_LOG_DEBUG(10),
                 "eref = %d, irefs = %d", zone->erefs, zone->irefs);
+       free_needed = exit_check(zone);
        UNLOCK_ZONE(zone);
-       exit_check(zone);
+       if (free_needed)
+               zone_free(zone);
 }
 
 isc_mem_t *
@@ -3562,12 +3570,17 @@ static void
 zone_shutdown(isc_task_t *task, isc_event_t *event) {
        dns_zone_t *zone = (dns_zone_t *) event->ev_arg;
        isc_result_t result;
+       isc_boolean_t free_needed;
 
        UNUSED(task);
        REQUIRE(DNS_ZONE_VALID(zone));
        INSIST(event->ev_type == DNS_EVENT_ZONECONTROL);
        INSIST(zone->erefs == 0);
        zone_log(zone, "zone_shutdown", ISC_LOG_DEBUG(3), "shutting down");
+
+       /*
+        * Stop things being restarted after we cancel them below.
+        */
        LOCK_ZONE(zone);
        DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_EXITING);
        UNLOCK_ZONE(zone);
@@ -3576,29 +3589,27 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) {
         * If we were waiting for xfrin quota, step out of
         * the queue.
         */
+       RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
        if (zone->statelist == &zone->zmgr->waiting_for_xfrin) {
-               RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
                ISC_LIST_UNLINK(zone->zmgr->waiting_for_xfrin, zone,
                                statelink);
-               RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
                zone->statelist = NULL;
        }
+       RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
+
 
+       LOCK_ZONE(zone);
        if (zone->xfr != NULL)
                dns_xfrin_shutdown(zone->xfr);
 
-       LOCK_ZONE(zone);
        if (zone->request != NULL) {
                dns_request_cancel(zone->request);
        }
-       UNLOCK_ZONE(zone);
 
        if (zone->readio != NULL)
                zonemgr_cancelio(zone->readio);
 
-       LOCK_ZONE(zone);
        notify_cancel(zone);
-       UNLOCK_ZONE(zone);
 
        if (zone->timer != NULL) {
                result = isc_timer_reset(zone->timer, isc_timertype_inactive,
@@ -3609,7 +3620,16 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) {
        if (zone->view != NULL)
                dns_view_weakdetach(&zone->view);
 
-       exit_check(zone);
+       /*
+        * We have now canceled everything set the flag to allow exit_check()
+        * to succeed.  We must not unlock between setting this flag and
+        * calling exit_check().
+        */
+       DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN);
+       free_needed = exit_check(zone);
+       UNLOCK_ZONE(zone);
+       if (free_needed)
+               zone_free(zone);
 }
 
 static void