Blame SOURCES/bind-9.11-CVE-2023-2828.patch

7cb01a
diff -up bind-9.11.4-P2/lib/dns/rbtdb.c.orig bind-9.11.4-P2/lib/dns/rbtdb.c
7cb01a
--- bind-9.11.4-P2/lib/dns/rbtdb.c.orig	2023-07-03 13:03:49.462352864 +0200
7cb01a
+++ bind-9.11.4-P2/lib/dns/rbtdb.c	2023-07-03 13:05:32.916227615 +0200
7cb01a
@@ -793,7 +793,7 @@ static void update_header(dns_rbtdb_t *r
7cb01a
 static void expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
7cb01a
 			  isc_boolean_t tree_locked, expire_t reason);
7cb01a
 static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
7cb01a
-			  isc_stdtime_t now, isc_boolean_t tree_locked);
7cb01a
+			  size_t purgesize, isc_boolean_t tree_locked);
7cb01a
 static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
7cb01a
 				  rdatasetheader_t *newheader);
7cb01a
 static void resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
7cb01a
@@ -6745,6 +6745,17 @@ addclosest(dns_rbtdb_t *rbtdb, rdataseth
7cb01a
 
7cb01a
 static dns_dbmethods_t zone_methods;
7cb01a
 
7cb01a
+static size_t
7cb01a
+rdataset_size(rdatasetheader_t *header) {
7cb01a
+	if (!NONEXISTENT(header)) {
7cb01a
+		return (dns_rdataslab_size((unsigned char *)header,
7cb01a
+					   sizeof(*header)));
7cb01a
+	}
7cb01a
+
7cb01a
+	return (sizeof(*header));
7cb01a
+}
7cb01a
+
7cb01a
+
7cb01a
 static isc_result_t
7cb01a
 addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
7cb01a
 	    isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
7cb01a
@@ -6885,7 +6896,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *
7cb01a
 	}
7cb01a
 
7cb01a
 	if (cache_is_overmem)
7cb01a
-		overmem_purge(rbtdb, rbtnode->locknum, now, tree_locked);
7cb01a
+		overmem_purge(rbtdb, rbtnode->locknum, rdataset_size(newheader),
7cb01a
+				  tree_locked);
7cb01a
 
7cb01a
 	NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
7cb01a
 		  isc_rwlocktype_write);
7cb01a
@@ -6900,10 +6912,14 @@ addrdataset(dns_db_t *db, dns_dbnode_t *
7cb01a
 			cleanup_dead_nodes(rbtdb, rbtnode->locknum);
7cb01a
 
7cb01a
 		header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1);
7cb01a
-		if (header && header->rdh_ttl < now - RBTDB_VIRTUAL)
7cb01a
-			expire_header(rbtdb, header, tree_locked,
7cb01a
-				      expire_ttl);
7cb01a
+		if (header != NULL) {
7cb01a
+			dns_ttl_t rdh_ttl = header->rdh_ttl;
7cb01a
 
7cb01a
+			if (rdh_ttl < now - RBTDB_VIRTUAL) {
7cb01a
+				expire_header(rbtdb, header, tree_locked,
7cb01a
+					      expire_ttl);
7cb01a
+			}
7cb01a
+		}
7cb01a
 		/*
7cb01a
 		 * If we've been holding a write lock on the tree just for
7cb01a
 		 * cleaning, we can release it now.  However, we still need the
7cb01a
@@ -10339,54 +10355,58 @@ update_header(dns_rbtdb_t *rbtdb, rdatas
7cb01a
 	ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum], header, link);
7cb01a
 }
7cb01a
 
7cb01a
+static size_t
7cb01a
+expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum, size_t purgesize,
7cb01a
+		   isc_boolean_t tree_locked) {
7cb01a
+	rdatasetheader_t *header, *header_prev;
7cb01a
+	size_t purged = 0;
7cb01a
+
7cb01a
+	for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
7cb01a
+	     header != NULL && purged <= purgesize; header = header_prev)
7cb01a
+	{
7cb01a
+		header_prev = ISC_LIST_PREV(header, link);
7cb01a
+		/*
7cb01a
+		 * Unlink the entry at this point to avoid checking it
7cb01a
+		 * again even if it's currently used someone else and
7cb01a
+		 * cannot be purged at this moment.  This entry won't be
7cb01a
+		 * referenced any more (so unlinking is safe) since the
7cb01a
+		 * TTL was reset to 0.
7cb01a
+		 */
7cb01a
+		ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header, link);
7cb01a
+		size_t header_size = rdataset_size(header);
7cb01a
+		expire_header(rbtdb, header, tree_locked, expire_lru);
7cb01a
+		purged += header_size;
7cb01a
+	}
7cb01a
+
7cb01a
+	return (purged);
7cb01a
+}
7cb01a
+
7cb01a
 /*%
7cb01a
- * Purge some expired and/or stale (i.e. unused for some period) cache entries
7cb01a
- * under an overmem condition.  To recover from this condition quickly, up to
7cb01a
- * 2 entries will be purged.  This process is triggered while adding a new
7cb01a
- * entry, and we specifically avoid purging entries in the same LRU bucket as
7cb01a
- * the one to which the new entry will belong.  Otherwise, we might purge
7cb01a
- * entries of the same name of different RR types while adding RRsets from a
7cb01a
- * single response (consider the case where we're adding A and AAAA glue records
7cb01a
- * of the same NS name).
7cb01a
+ * Purge some stale (i.e. unused for some period - LRU based cleaning) cache
7cb01a
+ * entries under the overmem condition.  To recover from this condition quickly,
7cb01a
+ * we cleanup entries up to the size of newly added rdata (passed as purgesize).
7cb01a
+ *
7cb01a
+ * This process is triggered while adding a new entry, and we specifically avoid
7cb01a
+ * purging entries in the same LRU bucket as the one to which the new entry will
7cb01a
+ * belong.  Otherwise, we might purge entries of the same name of different RR
7cb01a
+ * types while adding RRsets from a single response (consider the case where
7cb01a
+ * we're adding A and AAAA glue records of the same NS name).
7cb01a
  */
7cb01a
 static void
7cb01a
-overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
7cb01a
-	      isc_stdtime_t now, isc_boolean_t tree_locked)
7cb01a
+overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, size_t purgesize,
7cb01a
+		 isc_boolean_t tree_locked)
7cb01a
 {
7cb01a
-	rdatasetheader_t *header, *header_prev;
7cb01a
 	unsigned int locknum;
7cb01a
-	int purgecount = 2;
7cb01a
+	size_t purged = 0;
7cb01a
 
7cb01a
 	for (locknum = (locknum_start + 1) % rbtdb->node_lock_count;
7cb01a
-	     locknum != locknum_start && purgecount > 0;
7cb01a
+	     locknum != locknum_start && purged <= purgesize;
7cb01a
 	     locknum = (locknum + 1) % rbtdb->node_lock_count) {
7cb01a
 		NODE_LOCK(&rbtdb->node_locks[locknum].lock,
7cb01a
 			  isc_rwlocktype_write);
7cb01a
 
7cb01a
-		header = isc_heap_element(rbtdb->heaps[locknum], 1);
7cb01a
-		if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) {
7cb01a
-			expire_header(rbtdb, header, tree_locked,
7cb01a
-				      expire_ttl);
7cb01a
-			purgecount--;
7cb01a
-		}
7cb01a
-
7cb01a
-		for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
7cb01a
-		     header != NULL && purgecount > 0;
7cb01a
-		     header = header_prev) {
7cb01a
-			header_prev = ISC_LIST_PREV(header, link);
7cb01a
-			/*
7cb01a
-			 * Unlink the entry at this point to avoid checking it
7cb01a
-			 * again even if it's currently used someone else and
7cb01a
-			 * cannot be purged at this moment.  This entry won't be
7cb01a
-			 * referenced any more (so unlinking is safe) since the
7cb01a
-			 * TTL was reset to 0.
7cb01a
-			 */
7cb01a
-			ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header,
7cb01a
-					link);
7cb01a
-			expire_header(rbtdb, header, tree_locked,
7cb01a
-				      expire_lru);
7cb01a
-			purgecount--;
7cb01a
-		}
7cb01a
+		purged += expire_lru_headers(rbtdb, locknum, purgesize - purged,
7cb01a
+					     tree_locked);
7cb01a
 
7cb01a
 		NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
7cb01a
 				    isc_rwlocktype_write);