diff --git a/CHANGES b/CHANGES index 64cced24c8..ba22d0708d 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] + 4979. [bug] Non-libcap builds were not checking whether all requested capabilities are present in the permitted capability set. [GL #321] diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index b301b5b287..7f2a98b69a 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -90,6 +90,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 da70007f9a..9aab656d41 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.c,v 1.120 2011/12/22 07:32:41 each Exp $ */ - #include #include @@ -1013,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; @@ -1043,6 +1041,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); @@ -1096,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 || @@ -1145,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 >= 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 @@ -1670,7 +1689,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 7971f7ffa0..85b6008498 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -14281,8 +14281,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) { @@ -14306,6 +14312,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