- Fix perform a full transfer every number of incremental

transfers, to stop increasing memory usage, for auth-zone
  and rpz zones. Thanks to Qifan Zhang, Palo Alto Networks,
  for the report.
This commit is contained in:
W.C.A. Wijngaards
2026-06-15 15:51:03 +02:00
parent 153f8d5353
commit 1354624ba4
5 changed files with 34 additions and 2 deletions
+1
View File
@@ -3233,6 +3233,7 @@ do_auth_zone_reload(RES* ssl, struct worker* worker, char* arg)
z->zone_expired = 0;
if(xfr) {
xfr->zone_expired = 0;
xfr->num_ixfrs = 0;
if(!xfr_find_soa(z, xfr)) {
if(z->data.count == 0) {
lock_rw_unlock(&z->lock);
+4
View File
@@ -4,6 +4,10 @@
Default is disabled. This hardens against unbounded
transfers. Thanks to Qifan Zhang, Palo Alto Networks,
for the report.
- Fix perform a full transfer every number of incremental
transfers, to stop increasing memory usage, for auth-zone
and rpz zones. Thanks to Qifan Zhang, Palo Alto Networks,
for the report.
12 June 2026: Wouter
- Fix that for auth-zone and rpz zones the allow-notify
+4
View File
@@ -5130,6 +5130,10 @@ answer queries with that content.
because it may not have that when retrieving that data, instead use a plain
IP address to avoid a circular dependency on retrieving that IP address.
Every number of IXFR transfers, a full AXFR is performed.
This is to consolidate the rpz memory, that would otherwise grow.
The fixed value is after 5 IXFR transfers.
@@UAHL@unbound.conf.rpz@master@@: *<IP address or host name>*
Alternate syntax for :ref:`primary<unbound.conf.rpz.primary>`.
+21 -2
View File
@@ -96,6 +96,8 @@
/** number of timeouts before we fallback from IXFR to AXFR,
* because some versions of servers (eg. dnsmasq) drop IXFR packets. */
#define NUM_TIMEOUTS_FALLBACK_IXFR 3
/** number of IXFRs before an AXFR is performed, to consolidate RPZ memory. */
#define NUM_IXFR_BEFORE_AXFR 5
/** pick up nextprobe task to start waiting to perform transfer actions */
static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
@@ -2087,6 +2089,7 @@ auth_xfer_setup(struct auth_zone* z, struct auth_xfer* x)
if(!xfr_find_soa(z, x)) {
return 1;
}
x->is_rpz = (z->rpz!=NULL);
/* nothing for probe, nextprobe and transfer tasks */
return 1;
}
@@ -4317,7 +4320,7 @@ xfr_create_ixfr_packet(struct auth_xfer* xfr, sldns_buffer* buf, uint16_t id,
{
struct query_info qinfo;
uint32_t serial;
int have_zone;
int have_zone, get_full = 0;
have_zone = xfr->have_zone;
serial = xfr->serial;
@@ -4330,7 +4333,18 @@ xfr_create_ixfr_packet(struct auth_xfer* xfr, sldns_buffer* buf, uint16_t id,
xfr->task_transfer->on_ixfr_is_axfr = 0;
xfr->task_transfer->on_ixfr = 1;
qinfo.qtype = LDNS_RR_TYPE_IXFR;
if(!have_zone || xfr->task_transfer->ixfr_fail || !master->ixfr) {
if(xfr->num_ixfrs >= NUM_IXFR_BEFORE_AXFR && xfr->is_rpz) {
/* For the RPZ, an IXFR is going to grow regions, and a
* full transfer, zonefile read, AXFR and HTTP clear the
* region, but IXFR does not. That memory keeps growing,
* and getting a full transfer with AXFR here resets that.
* The rpz->client_set->region, rpz->ns_set->region and
* rpz->respip_set->region need to be reset, they are for
* rpz-client-ip, rpz-nsip and rpz-ip. */
get_full = 1;
}
if(!have_zone || xfr->task_transfer->ixfr_fail || !master->ixfr
|| get_full) {
qinfo.qtype = LDNS_RR_TYPE_AXFR;
xfr->task_transfer->ixfr_fail = 0;
xfr->task_transfer->on_ixfr = 0;
@@ -4955,6 +4969,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
int delmode = 0;
int softfail = 0;
xfr->num_ixfrs++;
/* start RR iterator over chunklist of packets */
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
@@ -5100,6 +5116,7 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
xfr->num_ixfrs = 0;
/* insert all RRs in to the zone */
/* insert the SOA only once, skip the last one */
@@ -5202,6 +5219,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
xfr->num_ixfrs = 0;
chunk = xfr->task_transfer->chunks_first;
chunk_pos = 0;
@@ -5414,6 +5432,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
}
z->soa_zone_acquired = *env->now;
xfr->soa_zone_acquired = *env->now;
xfr->is_rpz = (z->rpz!=NULL);
/* release xfr lock while verifying zonemd because it may have
* to spawn lookups in the state machines */
+4
View File
@@ -292,6 +292,10 @@ struct auth_xfer {
size_t max_transfer_size;
/** The maximum auth zone transfer time taken, in msec. */
int max_transfer_time;
/** the zone is an rpz zone */
int is_rpz;
/** the number of IXFRs since the last full transfer. */
int num_ixfrs;
};
/**