diff --git a/CHANGES b/CHANGES index 80800b8c62..f9601af521 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. [cleanup] Remove the deprecated flag from "answer-cookie"; it will be allowed to persist into 9.13. [GL #275]. diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 0cebfe9290..528ae6da7a 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -181,6 +181,14 @@
Bug Fixes + + + 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] + + rndc reload could cause named diff --git a/lib/dns/journal.c b/lib/dns/journal.c index 86764ce979..e44efc9598 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -9,8 +9,6 @@ * information regarding copyright ownership. */ -/* $Id: journal.c,v 1.120 2011/12/22 07:32:41 each Exp $ */ - #include #include @@ -1012,7 +1010,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; @@ -1042,6 +1040,14 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { size += t->rdata.length; } + if (size >= ISC_INT32_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); @@ -1095,6 +1101,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 || @@ -1144,6 +1151,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 >= ISC_INT32_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)ISC_INT32_MAX); + return (ISC_R_UNEXPECTED); + } + /* * Some old journal entries may become non-addressable * when we increment the current serial number. Purge them @@ -1659,7 +1678,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 b23c75a99a..9e81156ca0 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -14986,8 +14986,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 if (zone->journalsize != -1) { @@ -15011,6 +15017,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