[9.18] fix: usr: Recently expired records could be returned with timestamp in future

Under rare circumstances, the RRSet that expired at the time of
the query could be returned with TTL far in the future.  This
has been fixed.

As a side-effect, the expiration time of expired RRSets are no
longer printed out in the cache dump.

Closes #5094

Backport of MR !10048

Merge branch 'backport-5094-fix-timestamp-in-ttl-9.18' into 'bind-9.18'

See merge request isc-projects/bind9!10060
This commit is contained in:
Ondřej Surý 2025-02-03 15:17:42 +00:00
commit 4c49d99d56
3 changed files with 12 additions and 16 deletions

View file

@ -1612,7 +1612,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
# Check that expired records are not dumped.
ret=0
grep "; expired since .* (awaiting cleanup)" ns5/named_dump.db.test$n && ret=1
grep "; expired (awaiting cleanup)" ns5/named_dump.db.test$n && ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
@ -1628,13 +1628,13 @@ status=$((status + ret))
echo_i "check rndc dump expired data.example ($n)"
ret=0
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
| grep "; expired since .* (awaiting cleanup) data\.example\..*A text record with a 2 second ttl" >/dev/null 2>&1 || ret=1
| grep "; expired (awaiting cleanup) data\.example\..*A text record with a 2 second ttl" >/dev/null 2>&1 || ret=1
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
| grep "; expired since .* (awaiting cleanup) nodata\.example\." >/dev/null 2>&1 || ret=1
| grep "; expired (awaiting cleanup) nodata\.example\." >/dev/null 2>&1 || ret=1
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
| grep "; expired since .* (awaiting cleanup) nxdomain\.example\." >/dev/null 2>&1 || ret=1
| grep "; expired (awaiting cleanup) nxdomain\.example\." >/dev/null 2>&1 || ret=1
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
| grep "; expired since .* (awaiting cleanup) othertype\.example\." >/dev/null 2>&1 || ret=1
| grep "; expired (awaiting cleanup) othertype\.example\." >/dev/null 2>&1 || ret=1
# Also make sure the not expired data does not have an expired comment.
awk '/; authanswer/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
| grep "; authanswer longttl\.example.*A text record with a 600 second ttl" >/dev/null 2>&1 || ret=1

View file

@ -1157,15 +1157,7 @@ again:
if (STALE(rds)) {
fprintf(f, "; stale\n");
} else if (ANCIENT(rds)) {
isc_buffer_t b;
char buf[sizeof("YYYYMMDDHHMMSS")];
memset(buf, 0, sizeof(buf));
isc_buffer_init(&b, buf, sizeof(buf) - 1);
dns_time64_totext((uint64_t)rds->ttl, &b);
fprintf(f,
"; expired since %s "
"(awaiting cleanup)\n",
buf);
fprintf(f, "; expired (awaiting cleanup)\n");
}
result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
if (result != ISC_R_SUCCESS) {

View file

@ -3136,7 +3136,7 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
* (these records should not be cached anyway).
*/
if (KEEPSTALE(rbtdb) && stale_ttl > now) {
if (!ZEROTTL(header) && KEEPSTALE(rbtdb) && stale_ttl > now) {
stale = true;
} else {
/*
@ -3152,6 +3152,7 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
rdataset->type = RBTDB_RDATATYPE_BASE(header->type);
rdataset->covers = RBTDB_RDATATYPE_EXT(header->type);
rdataset->ttl = header->rdh_ttl - now;
rdataset->ttl = !ZEROTTL(header) ? header->rdh_ttl - now : 0;
rdataset->trust = header->trust;
if (NEGATIVE(header)) {
@ -3181,7 +3182,7 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
rdataset->attributes |= DNS_RDATASETATTR_STALE;
} else if (IS_CACHE(rbtdb) && !ACTIVE(header, now)) {
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
rdataset->ttl = header->rdh_ttl;
rdataset->ttl = 0;
}
rdataset->private1 = rbtdb;
@ -4689,6 +4690,7 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
}
free_rdataset(search->rbtdb, mctx, header);
} else {
set_ttl(search->rbtdb, header, 0);
mark_header_ancient(search->rbtdb, header);
*header_prev = header;
}
@ -5743,6 +5745,7 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
* refcurrent(rbtnode) must be non-zero. This is so
* because 'node' is an argument to the function.
*/
set_ttl(rbtdb, header, 0);
mark_header_ancient(rbtdb, header);
if (log) {
isc_log_write(dns_lctx, category, module, level,
@ -6034,6 +6037,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
* non-zero. This is so because 'node' is an
* argument to the function.
*/
set_ttl(rbtdb, header, 0);
mark_header_ancient(rbtdb, header);
}
} else if (EXISTS(header) && !ANCIENT(header)) {