diff --git a/CHANGES b/CHANGES index 318c9d32c8..a4dfc050b5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +4984. [bug] Improve handling of very large incremental + zone transfers to prevent journal corruption. [GL #339] + 4983. [func] Add the ability to not return a DNS COOKIE option when one is present in the request (answer-cookie no;). [GL #173] diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 42e724fa1d..36191cf434 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -316,7 +316,10 @@ - None. + named now rejects excessively large + incremental (IXFR) zone transfers in order to prevent + possible corruption of journal files which could cause + named to abort when loading zones. [GL #339] diff --git a/lib/dns/journal.c b/lib/dns/journal.c index d89ea4db1c..ff974cfb78 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -1011,7 +1011,7 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { dns_difftuple_t *t; isc_buffer_t buffer; void *mem = NULL; - unsigned int size; + isc_uint64_t size; isc_result_t result; isc_region_t used; @@ -1041,6 +1041,14 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { size += t->rdata.length; } + if (size >= DNS_JOURNAL_SIZE_MAX) { + isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, + "dns_journal_writediff: %s: journal entry " + "too big to be stored: %llu bytes", j->filename, + size); + return (ISC_R_NOSPACE); + } + mem = isc_mem_get(j->mctx, size); if (mem == NULL) return (ISC_R_NOMEMORY); @@ -1094,6 +1102,7 @@ isc_result_t dns_journal_commit(dns_journal_t *j) { isc_result_t result; journal_rawheader_t rawheader; + isc_uint64_t total; REQUIRE(DNS_JOURNAL_VALID(j)); REQUIRE(j->state == JOURNAL_STATE_TRANSACTION || @@ -1143,6 +1152,18 @@ dns_journal_commit(dns_journal_t *j) { } } + /* + * We currently don't support huge journal entries. + */ + total = j->x.pos[1].offset - j->x.pos[0].offset; + if (total >= DNS_JOURNAL_SIZE_MAX) { + isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, + "transaction too big to be stored in journal: " + "%llub (max is %llub)", total, + (isc_uint64_t)DNS_JOURNAL_SIZE_MAX); + return (ISC_R_UNEXPECTED); + } + /* * Some old journal entries may become non-addressable * when we increment the current serial number. Purge them @@ -1663,7 +1684,12 @@ read_one_rr(dns_journal_t *j) { journal_xhdr_t xhdr; journal_rrhdr_t rrhdr; - INSIST(j->offset <= j->it.epos.offset); + if (j->offset > j->it.epos.offset) { + isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, + "%s: journal corrupt: possible integer overflow", + j->filename); + return (ISC_R_UNEXPECTED); + } if (j->offset == j->it.epos.offset) return (ISC_R_NOMORE); if (j->it.xpos == j->it.xsize) { diff --git a/lib/dns/zone.c b/lib/dns/zone.c index e912a4047e..769664d07b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15032,8 +15032,14 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { result = dns_db_diff(zone->mctx, db, ver, zone->db, NULL, zone->journal); - if (result != ISC_R_SUCCESS) - goto fail; + if (result != ISC_R_SUCCESS) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + dns_zone_log(zone, ISC_LOG_ERROR, + "ixfr-from-differences: failed: " + "%s", strbuf); + goto fallback; + } if (dump) zone_needdump(zone, DNS_DUMP_DELAY); else @@ -15041,6 +15047,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { if (zone->type == dns_zone_master && inline_raw(zone)) zone_send_secureserial(zone, serial); } else { + fallback: if (dump && zone->masterfile != NULL) { /* * If DNS_ZONEFLG_FORCEXFER was set we don't want