diff --git a/CHANGES b/CHANGES index 4336d05dc9..f9262eac38 100644 --- a/CHANGES +++ b/CHANGES @@ -27,11 +27,15 @@ process of the catalog zone was already running. [GL #4136] + --- 9.19.14 released --- + 6192. [placeholder] 6191. [placeholder] -6190. [placeholder] +6190. [security] Improve the overmem cleaning process to prevent the + cache going over the configured limit. (CVE-2023-2828) + [GL #4055] 6189. [bug] Fix an extra dns_validator deatch when encountering deadling which would lead to assertion failure. diff --git a/bin/tests/system/hooks/tests_async_plugin.py b/bin/tests/system/hooks/tests_async_plugin.py index 48f9feefbd..2f42e27379 100644 --- a/bin/tests/system/hooks/tests_async_plugin.py +++ b/bin/tests/system/hooks/tests_async_plugin.py @@ -23,4 +23,5 @@ def test_async_hook(named_port): "A", ) ans = dns.query.udp(msg, "10.53.0.1", timeout=10, port=named_port) + # the test-async plugin changes the status of any positive answer to NOTIMP assert ans.rcode() == dns.rcode.NOTIMP diff --git a/doc/arm/notes.rst b/doc/arm/notes.rst index 8b8d64d332..13991e02ca 100644 --- a/doc/arm/notes.rst +++ b/doc/arm/notes.rst @@ -39,6 +39,7 @@ information about each release, and source code. .. include:: ../notes/notes-known-issues.rst .. include:: ../notes/notes-current.rst +.. include:: ../notes/notes-9.19.14.rst .. include:: ../notes/notes-9.19.13.rst .. include:: ../notes/notes-9.19.12.rst .. include:: ../notes/notes-9.19.11.rst diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 162c6cc470..ff400de38d 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -3772,6 +3772,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 diff --git a/doc/notes/notes-9.19.13.rst b/doc/notes/notes-9.19.13.rst index a88b3179c3..6438bc4e9a 100644 --- a/doc/notes/notes-9.19.13.rst +++ b/doc/notes/notes-9.19.13.rst @@ -29,11 +29,11 @@ Bug Fixes Furthermore, NOTIFY failures are now logged at the INFO level. :gl:`#4001` :gl:`#4002` -- The :any:`max-transfer-time-in` and :any:`max-transfer-idle-in` have - not had any effect since the BIND 9 networking stack was refactored in - version 9.16. The missing functionality has been re-implemented and - incoming zone transfers now time out properly when not progressing. - :gl:`#4004` +- The :any:`max-transfer-time-in` and :any:`max-transfer-idle-in` + statements have not had any effect since the BIND 9 networking stack + was refactored in version 9.16. The missing functionality has been + re-implemented and incoming zone transfers now time out properly when + not progressing. :gl:`#4004` - The read timeout in :iscman:`rndc` is now 60 seconds, matching the behavior in BIND 9.16 and earlier. It had previously been lowered to diff --git a/doc/notes/notes-9.19.14.rst b/doc/notes/notes-9.19.14.rst new file mode 100644 index 0000000000..3a270e5d79 --- /dev/null +++ b/doc/notes/notes-9.19.14.rst @@ -0,0 +1,89 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +Notes for BIND 9.19.14 +---------------------- + +Security Fixes +~~~~~~~~~~~~~~ + +- 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 +~~~~~~~~~~~~ + +- The read timeout in :iscman:`rndc` can now be specified on the command + line using the :option:`-t ` option, allowing commands that + take a long time to complete sufficient time to do so. :gl:`#4046` + +- Support for multi-signer model 2 (:rfc:`8901`) when using + :any:`inline-signing` was added. :gl:`#2710` + +- A new option to :any:`dnssec-policy` has been added, :any:`cdnskey`, + that allows users to enable or disable the publication of CDNSKEY + records. :gl:`#4050` + +- The system test suite can now be executed with pytest (along with + pytest-xdist for parallel execution). :gl:`#3978` + +Removed Features +~~~~~~~~~~~~~~~~ + +- Special-case code that was originally added to allow GSS-TSIG to work + around bugs in the Windows 2000 version of Active Directory has now + been removed, since Windows 2000 is long past end-of-life. The + :option:`-o ` option and the ``oldgsstsig`` command to + :iscman:`nsupdate` have been deprecated, and are now treated as + synonyms for :option:`-g ` and ``gsstsig`` respectively. + :gl:`#4012` + +Feature Changes +~~~~~~~~~~~~~~~ + +- If a response from an authoritative server has its RCODE set to + FORMERR and contains an echoed EDNS COOKIE option that was present in + the query, :iscman:`named` now retries sending the query to the + same server without an EDNS COOKIE option. :gl:`#4049` + +- The responsiveness of :iscman:`named` was improved, when serving as an + authoritative DNS server for a delegation-heavy zone(s) shortly after + loading such zone(s). :gl:`#4045` + +Bug Fixes +~~~~~~~~~ + +- When the :any:`stale-answer-enable` option was enabled and the + :any:`stale-answer-client-timeout` option was enabled and larger than + 0, :iscman:`named` previously allocated two slots from the + :any:`clients-per-query` limit for each client and failed to gradually + auto-tune its value, as configured. This has been fixed. :gl:`#4074` + +- Previously, it was possible for a delegation from cache to be returned + to the client after the :any:`stale-answer-client-timeout` duration. + This has been fixed. :gl:`#3950` + +- BIND could allocate too big buffers when sending data via + stream-based DNS transports, leading to increased memory usage. + This has been fixed. :gl:`#4038` + +Known Issues +~~~~~~~~~~~~ + +- There are no new known issues with this release. See :ref:`above + ` for a list of all known issues affecting this + BIND 9 branch. diff --git a/doc/notes/notes-known-issues.rst b/doc/notes/notes-known-issues.rst index d71ff3341c..e6622d56be 100644 --- a/doc/notes/notes-known-issues.rst +++ b/doc/notes/notes-known-issues.rst @@ -38,9 +38,3 @@ Known Issues have ``subjectAltName`` set. In such cases, the ``Subject`` field is ignored. Only old platforms are affected by this, e.g. those supplied with OpenSSL versions older than 1.1.1. :gl:`#3163` - -- Loading a large number of zones is significantly slower in BIND - 9.19.12 than in the previous development releases due to a new data - structure being used for storing information about the zones to serve. - This slowdown is considered to be a bug and will be addressed in a - future BIND 9.19.x development release. :gl:`#4006` diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 2bc92f3835..6887b9fcc4 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -622,7 +622,7 @@ expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, expire_t reason DNS__DB_FLARG); 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, isc_rwlocktype_t *tlocktypep DNS__DB_FLARG); static void resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader); @@ -6878,6 +6878,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, @@ -7042,7 +7052,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } if (cache_is_overmem) { - overmem_purge(rbtdb, rbtnode->locknum, now, + overmem_purge(rbtdb, rbtnode->locknum, rdataset_size(newheader), &tlocktype DNS__DB_FLARG_PASS); } @@ -7062,12 +7072,19 @@ 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, &nlocktype, &tlocktype, - expire_ttl DNS__DB_FLARG_PASS); + 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, &nlocktype, + &tlocktype, + expire_ttl DNS__DB_FLARG_PASS); + } } /* @@ -9971,54 +9988,61 @@ 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, + isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, + size_t purgesize DNS__DB_FLARG) { + 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, nlocktypep, tlocktypep, + expire_lru DNS__DB_FLARG_PASS); + 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, isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) { - 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) { isc_rwlocktype_t nlocktype = isc_rwlocktype_none; NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - header = isc_heap_element(rbtdb->heaps[locknum], 1); - if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) { - expire_header(rbtdb, header, &nlocktype, tlocktypep, - expire_ttl DNS__DB_FLARG_PASS); - 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, &nlocktype, tlocktypep, - expire_lru DNS__DB_FLARG_PASS); - purgecount--; - } + purged += expire_lru_headers( + rbtdb, locknum, &nlocktype, tlocktypep, + purgesize - purged DNS__DB_FLARG_PASS); NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); } @@ -10037,15 +10061,14 @@ expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, INSIST(*nlocktypep == isc_rwlocktype_write); if (isc_refcount_current(&header->node->references) == 0) { - isc_rwlocktype_t nlocktype = isc_rwlocktype_write; /* * If no one else is using the node, we can clean it up now. * We first need to gain a new reference to the node to meet a * requirement of decrement_reference(). */ new_reference(rbtdb, header->node, - nlocktype DNS__DB_FLARG_PASS); - decrement_reference(rbtdb, header->node, 0, &nlocktype, + *nlocktypep DNS__DB_FLARG_PASS); + decrement_reference(rbtdb, header->node, 0, nlocktypep, tlocktypep, true, false DNS__DB_FLARG_PASS); if (rbtdb->cachestats == NULL) {