mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-29 13:38:26 +00:00
Merge branch '4055-improve-the-overmem-cache-cleaning-9.18' into 'security-bind-9.18'
[9.18] Improve RBT overmem cache cleaning See merge request isc-private/bind9!527
This commit is contained in:
commit
e9af3d15d8
4
CHANGES
4
CHANGES
@ -1,3 +1,7 @@
|
||||
6190. [security] Improve the overmem cleaning process to prevent the
|
||||
cache going over the configured limit. (CVE-2023-2828)
|
||||
[GL #4055]
|
||||
|
||||
6188. [performance] Reduce memory consumption by allocating properly
|
||||
sized send buffers for stream-based transports.
|
||||
[GL #4038]
|
||||
|
@ -3932,6 +3932,11 @@ system.
|
||||
default value of that option (90% of physical memory for each
|
||||
individual cache) may lead to memory exhaustion over time.
|
||||
|
||||
.. note::
|
||||
|
||||
:any:`max-cache-size` does not work reliably for the maximum
|
||||
amount of memory of 100 MB or lower.
|
||||
|
||||
Upon startup and reconfiguration, caches with a limited size
|
||||
preallocate a small amount of memory (less than 1% of
|
||||
:any:`max-cache-size` for a given view). This preallocation serves as an
|
||||
|
@ -15,7 +15,14 @@ Notes for BIND 9.18.16
|
||||
Security Fixes
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
- None.
|
||||
- The overmem cleaning process has been improved, to prevent the cache from
|
||||
significantly exceeding the configured :any:`max-cache-size` limit.
|
||||
(CVE-2023-2828)
|
||||
|
||||
ISC would like to thank Shoham Danino from Reichman University, Anat
|
||||
Bremler-Barr from Tel-Aviv University, Yehuda Afek from Tel-Aviv University,
|
||||
and Yuval Shavitt from Tel-Aviv University for bringing this vulnerability to
|
||||
our attention. :gl:`#4055`
|
||||
|
||||
New Features
|
||||
~~~~~~~~~~~~
|
||||
|
106
lib/dns/rbtdb.c
106
lib/dns/rbtdb.c
@ -561,7 +561,7 @@ static void
|
||||
expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, bool tree_locked,
|
||||
expire_t reason);
|
||||
static void
|
||||
overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, isc_stdtime_t now,
|
||||
overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, size_t purgesize,
|
||||
bool tree_locked);
|
||||
static void
|
||||
resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader);
|
||||
@ -6795,6 +6795,16 @@ cleanup:
|
||||
|
||||
static dns_dbmethods_t zone_methods;
|
||||
|
||||
static size_t
|
||||
rdataset_size(rdatasetheader_t *header) {
|
||||
if (!NONEXISTENT(header)) {
|
||||
return (dns_rdataslab_size((unsigned char *)header,
|
||||
sizeof(*header)));
|
||||
}
|
||||
|
||||
return (sizeof(*header));
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
||||
isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
|
||||
@ -6959,7 +6969,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
||||
}
|
||||
|
||||
if (cache_is_overmem) {
|
||||
overmem_purge(rbtdb, rbtnode->locknum, now, tree_locked);
|
||||
overmem_purge(rbtdb, rbtnode->locknum, rdataset_size(newheader),
|
||||
tree_locked);
|
||||
}
|
||||
|
||||
NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
|
||||
@ -6978,11 +6989,18 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
||||
}
|
||||
|
||||
header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1);
|
||||
if (header != NULL &&
|
||||
header->rdh_ttl + STALE_TTL(header, rbtdb) <
|
||||
now - RBTDB_VIRTUAL)
|
||||
{
|
||||
expire_header(rbtdb, header, tree_locked, expire_ttl);
|
||||
if (header != NULL) {
|
||||
dns_ttl_t rdh_ttl = header->rdh_ttl;
|
||||
|
||||
/* Only account for stale TTL if cache is not overmem */
|
||||
if (!cache_is_overmem) {
|
||||
rdh_ttl += STALE_TTL(header, rbtdb);
|
||||
}
|
||||
|
||||
if (rdh_ttl < now - RBTDB_VIRTUAL) {
|
||||
expire_header(rbtdb, header, tree_locked,
|
||||
expire_ttl);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -10122,52 +10140,58 @@ update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, isc_stdtime_t now) {
|
||||
ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum], header, link);
|
||||
}
|
||||
|
||||
static size_t
|
||||
expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum, size_t purgesize,
|
||||
bool tree_locked) {
|
||||
rdatasetheader_t *header, *header_prev;
|
||||
size_t purged = 0;
|
||||
|
||||
for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
|
||||
header != NULL && purged <= purgesize; header = header_prev)
|
||||
{
|
||||
header_prev = ISC_LIST_PREV(header, link);
|
||||
/*
|
||||
* Unlink the entry at this point to avoid checking it
|
||||
* again even if it's currently used someone else and
|
||||
* cannot be purged at this moment. This entry won't be
|
||||
* referenced any more (so unlinking is safe) since the
|
||||
* TTL was reset to 0.
|
||||
*/
|
||||
ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header, link);
|
||||
size_t header_size = rdataset_size(header);
|
||||
expire_header(rbtdb, header, tree_locked, expire_lru);
|
||||
purged += header_size;
|
||||
}
|
||||
|
||||
return (purged);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Purge some expired and/or stale (i.e. unused for some period) cache entries
|
||||
* under an overmem condition. To recover from this condition quickly, up to
|
||||
* 2 entries will be purged. This process is triggered while adding a new
|
||||
* entry, and we specifically avoid purging entries in the same LRU bucket as
|
||||
* the one to which the new entry will belong. Otherwise, we might purge
|
||||
* entries of the same name of different RR types while adding RRsets from a
|
||||
* single response (consider the case where we're adding A and AAAA glue records
|
||||
* of the same NS name).
|
||||
* Purge some stale (i.e. unused for some period - LRU based cleaning) cache
|
||||
* entries under the overmem condition. To recover from this condition quickly,
|
||||
* we cleanup entries up to the size of newly added rdata (passed as purgesize).
|
||||
*
|
||||
* This process is triggered while adding a new entry, and we specifically avoid
|
||||
* purging entries in the same LRU bucket as the one to which the new entry will
|
||||
* belong. Otherwise, we might purge entries of the same name of different RR
|
||||
* types while adding RRsets from a single response (consider the case where
|
||||
* we're adding A and AAAA glue records of the same NS name).
|
||||
*/
|
||||
static void
|
||||
overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, isc_stdtime_t now,
|
||||
overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, size_t purgesize,
|
||||
bool tree_locked) {
|
||||
rdatasetheader_t *header, *header_prev;
|
||||
unsigned int locknum;
|
||||
int purgecount = 2;
|
||||
size_t purged = 0;
|
||||
|
||||
for (locknum = (locknum_start + 1) % rbtdb->node_lock_count;
|
||||
locknum != locknum_start && purgecount > 0;
|
||||
locknum != locknum_start && purged <= purgesize;
|
||||
locknum = (locknum + 1) % rbtdb->node_lock_count)
|
||||
{
|
||||
NODE_LOCK(&rbtdb->node_locks[locknum].lock,
|
||||
isc_rwlocktype_write);
|
||||
|
||||
header = isc_heap_element(rbtdb->heaps[locknum], 1);
|
||||
if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) {
|
||||
expire_header(rbtdb, header, tree_locked, expire_ttl);
|
||||
purgecount--;
|
||||
}
|
||||
|
||||
for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
|
||||
header != NULL && purgecount > 0; header = header_prev)
|
||||
{
|
||||
header_prev = ISC_LIST_PREV(header, link);
|
||||
/*
|
||||
* Unlink the entry at this point to avoid checking it
|
||||
* again even if it's currently used someone else and
|
||||
* cannot be purged at this moment. This entry won't be
|
||||
* referenced any more (so unlinking is safe) since the
|
||||
* TTL was reset to 0.
|
||||
*/
|
||||
ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header,
|
||||
link);
|
||||
expire_header(rbtdb, header, tree_locked, expire_lru);
|
||||
purgecount--;
|
||||
}
|
||||
purged += expire_lru_headers(rbtdb, locknum, purgesize - purged,
|
||||
tree_locked);
|
||||
|
||||
NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
|
||||
isc_rwlocktype_write);
|
||||
|
Loading…
x
Reference in New Issue
Block a user