All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] bind: 2 CVE fixes
@ 2017-04-13  5:20 Yi Zhao
  2017-04-13  5:20 ` [PATCH 1/2] bind: Security fix CVE-2016-8864 Yi Zhao
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Yi Zhao @ 2017-04-13  5:20 UTC (permalink / raw)
  To: openembedded-core

Fix CVE-2016-8864, CVE-2016-6170

Yi Zhao (2):
  bind: Security fix CVE-2016-8864
  bind: Security fix CVE-2016-6170

 .../bind/bind/CVE-2016-6170.patch                  | 1088 ++++++++++++++++++++
 .../bind/bind/CVE-2016-8864.patch                  |  217 ++++
 meta/recipes-connectivity/bind/bind_9.10.3-P3.bb   |    2 +
 3 files changed, 1307 insertions(+)
 create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch
 create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2016-8864.patch

-- 
2.7.4



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] bind: Security fix CVE-2016-8864
  2017-04-13  5:20 [PATCH 0/2] bind: 2 CVE fixes Yi Zhao
@ 2017-04-13  5:20 ` Yi Zhao
  2017-04-13  5:20 ` [PATCH 2/2] bind: Security fix CVE-2016-6170 Yi Zhao
  2017-04-13  5:32 ` ✗ patchtest: failure for bind: 2 CVE fixes Patchwork
  2 siblings, 0 replies; 4+ messages in thread
From: Yi Zhao @ 2017-04-13  5:20 UTC (permalink / raw)
  To: openembedded-core

CVE-2016-8864: named in ISC BIND 9.x before 9.9.9-P4, 9.10.x before
9.10.4-P4, and 9.11.x before 9.11.0-P1 allows remote attackers to cause
a denial of service (assertion failure and daemon exit) via a DNAME
record in the answer section of a response to a recursive query,
related to db.c and resolver.c.

External References:
https://nvd.nist.gov/vuln/detail/CVE-2016-8864

Patch from:
https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=c1d0599a246f646d1c22018f8fa09459270a44b8

Signed-off-by: Yi Zhao <yi.zhao@windriver.com>
---
 .../bind/bind/CVE-2016-8864.patch                  | 217 +++++++++++++++++++++
 meta/recipes-connectivity/bind/bind_9.10.3-P3.bb   |   1 +
 2 files changed, 218 insertions(+)
 create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2016-8864.patch

diff --git a/meta/recipes-connectivity/bind/bind/CVE-2016-8864.patch b/meta/recipes-connectivity/bind/bind/CVE-2016-8864.patch
new file mode 100644
index 0000000..5bd0d7a
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2016-8864.patch
@@ -0,0 +1,217 @@
+From c1d0599a246f646d1c22018f8fa09459270a44b8 Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Fri, 21 Oct 2016 14:55:10 +1100
+Subject: [PATCH] 4489. [security] It was possible to trigger assertions when
+ processing a response. (CVE-2016-8864) [RT #43465]
+
+(cherry picked from commit bd6f27f5c353133b563fe69100b2f168c129f3ca)
+
+Upstream-Status: Backport
+[https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=c1d0599a246f646d1c22018f8fa09459270a44b8]
+
+Signed-off-by: Yi Zhao <yi.zhao@windriver.com>
+---
+ CHANGES            |  3 +++
+ lib/dns/resolver.c | 69 +++++++++++++++++++++++++++++++++++++-----------------
+ 2 files changed, 50 insertions(+), 22 deletions(-)
+
+diff --git a/CHANGES b/CHANGES
+index 5c8c61a..41cfce5 100644
+--- a/CHANGES
++++ b/CHANGES
+@@ -1,3 +1,6 @@
++4489.	[security]	It was possible to trigger assertions when processing
++			a response. (CVE-2016-8864) [RT #43465]
++
+ 4467.   [security]      It was possible to trigger an assertion when
+                         rendering a message. (CVE-2016-2776) [RT #43139]
+ 
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index ba1ae23..13c8b44 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -612,7 +612,9 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
+ 	valarg->addrinfo = addrinfo;
+ 
+ 	if (!ISC_LIST_EMPTY(fctx->validators))
+-		INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0);
++		valoptions |= DNS_VALIDATOR_DEFER;
++	else
++		valoptions &= ~DNS_VALIDATOR_DEFER;
+ 
+ 	result = dns_validator_create(fctx->res->view, name, type, rdataset,
+ 				      sigrdataset, fctx->rmessage,
+@@ -5526,13 +5528,6 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
+ 							   rdataset,
+ 							   sigrdataset,
+ 							   valoptions, task);
+-					/*
+-					 * Defer any further validations.
+-					 * This prevents multiple validators
+-					 * from manipulating fctx->rmessage
+-					 * simultaneously.
+-					 */
+-					valoptions |= DNS_VALIDATOR_DEFER;
+ 				}
+ 			} else if (CHAINING(rdataset)) {
+ 				if (rdataset->type == dns_rdatatype_cname)
+@@ -5647,6 +5642,11 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
+ 				       eresult == DNS_R_NCACHENXRRSET);
+ 			}
+ 			event->result = eresult;
++			if (adbp != NULL && *adbp != NULL) {
++				if (anodep != NULL && *anodep != NULL)
++					dns_db_detachnode(*adbp, anodep);
++				dns_db_detach(adbp);
++			}
+ 			dns_db_attach(fctx->cache, adbp);
+ 			dns_db_transfernode(fctx->cache, &node, anodep);
+ 			clone_results(fctx);
+@@ -5897,6 +5897,11 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
+ 		fctx->attributes |= FCTX_ATTR_HAVEANSWER;
+ 		if (event != NULL) {
+ 			event->result = eresult;
++			if (adbp != NULL && *adbp != NULL) {
++				if (anodep != NULL && *anodep != NULL)
++					dns_db_detachnode(*adbp, anodep);
++				dns_db_detach(adbp);
++			}
+ 			dns_db_attach(fctx->cache, adbp);
+ 			dns_db_transfernode(fctx->cache, &node, anodep);
+ 			clone_results(fctx);
+@@ -6718,13 +6723,15 @@ static isc_result_t
+ answer_response(fetchctx_t *fctx) {
+ 	isc_result_t result;
+ 	dns_message_t *message;
+-	dns_name_t *name, *dname, *qname, tname, *ns_name;
++	dns_name_t *name, *dname = NULL, *qname, *dqname, tname, *ns_name;
++	dns_name_t *cname = NULL;
+ 	dns_rdataset_t *rdataset, *ns_rdataset;
+ 	isc_boolean_t done, external, chaining, aa, found, want_chaining;
+-	isc_boolean_t have_answer, found_cname, found_type, wanted_chaining;
++	isc_boolean_t have_answer, found_cname, found_dname, found_type;
++	isc_boolean_t wanted_chaining;
+ 	unsigned int aflag;
+ 	dns_rdatatype_t type;
+-	dns_fixedname_t fdname, fqname;
++	dns_fixedname_t fdname, fqname, fqdname;
+ 	dns_view_t *view;
+ 
+ 	FCTXTRACE("answer_response");
+@@ -6738,6 +6745,7 @@ answer_response(fetchctx_t *fctx) {
+ 
+ 	done = ISC_FALSE;
+ 	found_cname = ISC_FALSE;
++	found_dname = ISC_FALSE;
+ 	found_type = ISC_FALSE;
+ 	chaining = ISC_FALSE;
+ 	have_answer = ISC_FALSE;
+@@ -6747,12 +6755,13 @@ answer_response(fetchctx_t *fctx) {
+ 		aa = ISC_TRUE;
+ 	else
+ 		aa = ISC_FALSE;
+-	qname = &fctx->name;
++	dqname = qname = &fctx->name;
+ 	type = fctx->type;
+ 	view = fctx->res->view;
++	dns_fixedname_init(&fqdname);
+ 	result = dns_message_firstname(message, DNS_SECTION_ANSWER);
+ 	while (!done && result == ISC_R_SUCCESS) {
+-		dns_namereln_t namereln;
++		dns_namereln_t namereln, dnamereln;
+ 		int order;
+ 		unsigned int nlabels;
+ 
+@@ -6760,6 +6769,8 @@ answer_response(fetchctx_t *fctx) {
+ 		dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
+ 		external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
+ 		namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
++		dnamereln = dns_name_fullcompare(dqname, name, &order,
++						 &nlabels);
+ 		if (namereln == dns_namereln_equal) {
+ 			wanted_chaining = ISC_FALSE;
+ 			for (rdataset = ISC_LIST_HEAD(name->list);
+@@ -6854,7 +6865,7 @@ answer_response(fetchctx_t *fctx) {
+ 					}
+ 				} else if (rdataset->type == dns_rdatatype_rrsig
+ 					   && rdataset->covers ==
+-					   dns_rdatatype_cname
++					      dns_rdatatype_cname
+ 					   && !found_type) {
+ 					/*
+ 					 * We're looking for something else,
+@@ -6884,11 +6895,18 @@ answer_response(fetchctx_t *fctx) {
+ 						 * a CNAME or DNAME).
+ 						 */
+ 						INSIST(!external);
+-						if (aflag ==
+-						    DNS_RDATASETATTR_ANSWER) {
++						if ((rdataset->type !=
++						     dns_rdatatype_cname) ||
++						    !found_dname ||
++						    (aflag ==
++						     DNS_RDATASETATTR_ANSWER))
++						{
+ 							have_answer = ISC_TRUE;
++							if (rdataset->type ==
++							    dns_rdatatype_cname)
++								cname = name;
+ 							name->attributes |=
+-								DNS_NAMEATTR_ANSWER;
++							    DNS_NAMEATTR_ANSWER;
+ 						}
+ 						rdataset->attributes |= aflag;
+ 						if (aa)
+@@ -6982,11 +7000,11 @@ answer_response(fetchctx_t *fctx) {
+ 					return (DNS_R_FORMERR);
+ 				}
+ 
+-				if (namereln != dns_namereln_subdomain) {
++				if (dnamereln != dns_namereln_subdomain) {
+ 					char qbuf[DNS_NAME_FORMATSIZE];
+ 					char obuf[DNS_NAME_FORMATSIZE];
+ 
+-					dns_name_format(qname, qbuf,
++					dns_name_format(dqname, qbuf,
+ 							sizeof(qbuf));
+ 					dns_name_format(name, obuf,
+ 							sizeof(obuf));
+@@ -7001,7 +7019,7 @@ answer_response(fetchctx_t *fctx) {
+ 					want_chaining = ISC_TRUE;
+ 					POST(want_chaining);
+ 					aflag = DNS_RDATASETATTR_ANSWER;
+-					result = dname_target(rdataset, qname,
++					result = dname_target(rdataset, dqname,
+ 							      nlabels, &fdname);
+ 					if (result == ISC_R_NOSPACE) {
+ 						/*
+@@ -7018,10 +7036,13 @@ answer_response(fetchctx_t *fctx) {
+ 
+ 					dname = dns_fixedname_name(&fdname);
+ 					if (!is_answertarget_allowed(view,
+-							qname, rdataset->type,
+-							dname, &fctx->domain)) {
++						     dqname, rdataset->type,
++						     dname, &fctx->domain))
++					{
+ 						return (DNS_R_SERVFAIL);
+ 					}
++					dqname = dns_fixedname_name(&fqdname);
++					dns_name_copy(dname, dqname, NULL);
+ 				} else {
+ 					/*
+ 					 * We've found a signature that
+@@ -7046,6 +7067,10 @@ answer_response(fetchctx_t *fctx) {
+ 					INSIST(!external);
+ 					if (aflag == DNS_RDATASETATTR_ANSWER) {
+ 						have_answer = ISC_TRUE;
++						found_dname = ISC_TRUE;
++						if (cname != NULL)
++							cname->attributes &=
++							   ~DNS_NAMEATTR_ANSWER;
+ 						name->attributes |=
+ 							DNS_NAMEATTR_ANSWER;
+ 					}
+-- 
+2.7.4
+
diff --git a/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb b/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
index 4e2e856..fa45809 100644
--- a/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
+++ b/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
@@ -27,6 +27,7 @@ SRC_URI = "ftp://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.gz \
            file://CVE-2016-2088.patch \
            file://CVE-2016-2775.patch \
            file://CVE-2016-2776.patch \
+           file://CVE-2016-8864.patch \
            "
 
 SRC_URI[md5sum] = "bcf7e772b616f7259420a3edc5df350a"
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] bind: Security fix CVE-2016-6170
  2017-04-13  5:20 [PATCH 0/2] bind: 2 CVE fixes Yi Zhao
  2017-04-13  5:20 ` [PATCH 1/2] bind: Security fix CVE-2016-8864 Yi Zhao
@ 2017-04-13  5:20 ` Yi Zhao
  2017-04-13  5:32 ` ✗ patchtest: failure for bind: 2 CVE fixes Patchwork
  2 siblings, 0 replies; 4+ messages in thread
From: Yi Zhao @ 2017-04-13  5:20 UTC (permalink / raw)
  To: openembedded-core

CVE-2016-6170: ISC BIND through 9.9.9-P1, 9.10.x through 9.10.4-P1, and
9.11.x through 9.11.0b1 allows primary DNS servers to cause a denial of
service (secondary DNS server crash) via a large AXFR response, and
possibly allows IXFR servers to cause a denial of service (IXFR client
crash) via a large IXFR response and allows remote authenticated users
to cause a denial of service (primary DNS server crash) via a large
UPDATE message.

External References:
https://nvd.nist.gov/vuln/detail/CVE-2016-6170

Patch from:
https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f

Signed-off-by: Yi Zhao <yi.zhao@windriver.com>
---
 .../bind/bind/CVE-2016-6170.patch                  | 1088 ++++++++++++++++++++
 meta/recipes-connectivity/bind/bind_9.10.3-P3.bb   |    1 +
 2 files changed, 1089 insertions(+)
 create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch

diff --git a/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch b/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch
new file mode 100644
index 0000000..e0653bf
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch
@@ -0,0 +1,1088 @@
+From 1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 2 Nov 2016 17:31:27 +1100
+Subject: [PATCH] 4504. [security] Allow the maximum number of records in a
+ zone to be specified. This provides a control for issues raised in
+ CVE-2016-6170. [RT #42143]
+
+(cherry picked from commit 5f8412a4cb5ee14a0e8cddd4107854b40ee3291e)
+
+Upstream-Status: Backport
+[https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f]
+
+Signed-off-by: Yi Zhao <yi.zhao@windriver.com>
+---
+ CHANGES                                          |   4 +
+ bin/named/config.c                               |   1 +
+ bin/named/named.conf.docbook                     |   3 +
+ bin/named/update.c                               |  16 +++
+ bin/named/zoneconf.c                             |   7 ++
+ bin/tests/system/nsupdate/clean.sh               |   1 +
+ bin/tests/system/nsupdate/ns3/named.conf         |   7 ++
+ bin/tests/system/nsupdate/ns3/too-big.test.db.in |  10 ++
+ bin/tests/system/nsupdate/setup.sh               |   2 +
+ bin/tests/system/nsupdate/tests.sh               |  15 +++
+ bin/tests/system/xfer/clean.sh                   |   1 +
+ bin/tests/system/xfer/ns1/axfr-too-big.db        |  10 ++
+ bin/tests/system/xfer/ns1/ixfr-too-big.db.in     |  13 +++
+ bin/tests/system/xfer/ns1/named.conf             |  11 ++
+ bin/tests/system/xfer/ns6/named.conf             |  14 +++
+ bin/tests/system/xfer/setup.sh                   |   2 +
+ bin/tests/system/xfer/tests.sh                   |  26 +++++
+ doc/arm/Bv9ARM-book.xml                          |  21 ++++
+ doc/arm/notes.xml                                |   9 ++
+ lib/bind9/check.c                                |   2 +
+ lib/dns/db.c                                     |  13 +++
+ lib/dns/ecdb.c                                   |   3 +-
+ lib/dns/include/dns/db.h                         |  20 ++++
+ lib/dns/include/dns/rdataslab.h                  |  13 +++
+ lib/dns/include/dns/result.h                     |   6 +-
+ lib/dns/include/dns/zone.h                       |  28 ++++-
+ lib/dns/rbtdb.c                                  | 127 +++++++++++++++++++++--
+ lib/dns/rdataslab.c                              |  13 +++
+ lib/dns/result.c                                 |   9 +-
+ lib/dns/sdb.c                                    |   3 +-
+ lib/dns/sdlz.c                                   |   3 +-
+ lib/dns/xfrin.c                                  |  22 +++-
+ lib/dns/zone.c                                   |  23 +++-
+ lib/isccfg/namedconf.c                           |   1 +
+ 34 files changed, 444 insertions(+), 15 deletions(-)
+ create mode 100644 bin/tests/system/nsupdate/ns3/too-big.test.db.in
+ create mode 100644 bin/tests/system/xfer/ns1/axfr-too-big.db
+ create mode 100644 bin/tests/system/xfer/ns1/ixfr-too-big.db.in
+
+diff --git a/CHANGES b/CHANGES
+index 41cfce5..97d2e60 100644
+--- a/CHANGES
++++ b/CHANGES
+@@ -1,3 +1,7 @@
++4504.	[security]	Allow the maximum number of records in a zone to
++			be specified.  This provides a control for issues
++			raised in CVE-2016-6170. [RT #42143]
++
+ 4489.	[security]	It was possible to trigger assertions when processing
+ 			a response. (CVE-2016-8864) [RT #43465]
+ 
+diff --git a/bin/named/config.c b/bin/named/config.c
+index f06348c..c24e334 100644
+--- a/bin/named/config.c
++++ b/bin/named/config.c
+@@ -209,6 +209,7 @@ options {\n\
+ 	max-transfer-time-out 120;\n\
+ 	max-transfer-idle-in 60;\n\
+ 	max-transfer-idle-out 60;\n\
++	max-records 0;\n\
+ 	max-retry-time 1209600; /* 2 weeks */\n\
+ 	min-retry-time 500;\n\
+ 	max-refresh-time 2419200; /* 4 weeks */\n\
+diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook
+index 4c99a61..c2d173a 100644
+--- a/bin/named/named.conf.docbook
++++ b/bin/named/named.conf.docbook
+@@ -338,6 +338,7 @@ options {
+ 	};
+ 
+ 	max-journal-size <replaceable>size_no_default</replaceable>;
++	max-records <replaceable>integer</replaceable>;
+ 	max-transfer-time-in <replaceable>integer</replaceable>;
+ 	max-transfer-time-out <replaceable>integer</replaceable>;
+ 	max-transfer-idle-in <replaceable>integer</replaceable>;
+@@ -527,6 +528,7 @@ view <replaceable>string</replaceable> <replaceable>optional_class</replaceable>
+ 	};
+ 
+ 	max-journal-size <replaceable>size_no_default</replaceable>;
++	max-records <replaceable>integer</replaceable>;
+ 	max-transfer-time-in <replaceable>integer</replaceable>;
+ 	max-transfer-time-out <replaceable>integer</replaceable>;
+ 	max-transfer-idle-in <replaceable>integer</replaceable>;
+@@ -624,6 +626,7 @@ zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable>
+ 	};
+ 
+ 	max-journal-size <replaceable>size_no_default</replaceable>;
++	max-records <replaceable>integer</replaceable>;
+ 	max-transfer-time-in <replaceable>integer</replaceable>;
+ 	max-transfer-time-out <replaceable>integer</replaceable>;
+ 	max-transfer-idle-in <replaceable>integer</replaceable>;
+diff --git a/bin/named/update.c b/bin/named/update.c
+index 83b1a05..cc2a611 100644
+--- a/bin/named/update.c
++++ b/bin/named/update.c
+@@ -2455,6 +2455,8 @@ update_action(isc_task_t *task, isc_event_t *event) {
+ 	isc_boolean_t had_dnskey;
+ 	dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+ 	dns_ttl_t maxttl = 0;
++	isc_uint32_t maxrecords;
++	isc_uint64_t records;
+ 
+ 	INSIST(event->ev_type == DNS_EVENT_UPDATE);
+ 
+@@ -3138,6 +3140,20 @@ update_action(isc_task_t *task, isc_event_t *event) {
+ 			}
+ 		}
+ 
++		maxrecords = dns_zone_getmaxrecords(zone);
++		if (maxrecords != 0U) {
++			result = dns_db_getsize(db, ver, &records, NULL);
++			if (result == ISC_R_SUCCESS && records > maxrecords) {
++				update_log(client, zone, ISC_LOG_ERROR,
++					   "records in zone (%"
++					   ISC_PRINT_QUADFORMAT
++					   "u) exceeds max-records (%u)",
++					   records, maxrecords);
++				result = DNS_R_TOOMANYRECORDS;
++				goto failure;
++			}
++		}
++
+ 		journalfile = dns_zone_getjournal(zone);
+ 		if (journalfile != NULL) {
+ 			update_log(client, zone, LOGLEVEL_DEBUG,
+diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
+index 4ee3dfe..14dd8ce 100644
+--- a/bin/named/zoneconf.c
++++ b/bin/named/zoneconf.c
+@@ -978,6 +978,13 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ 			dns_zone_setmaxttl(raw, maxttl);
+ 	}
+ 
++	obj = NULL;
++	result = ns_config_get(maps, "max-records", &obj);
++	INSIST(result == ISC_R_SUCCESS && obj != NULL);
++	dns_zone_setmaxrecords(mayberaw, cfg_obj_asuint32(obj));
++	if (zone != mayberaw)
++		dns_zone_setmaxrecords(zone, 0);
++
+ 	if (raw != NULL && filename != NULL) {
+ #define SIGNED ".signed"
+ 		size_t signedlen = strlen(filename) + sizeof(SIGNED);
+diff --git a/bin/tests/system/nsupdate/clean.sh b/bin/tests/system/nsupdate/clean.sh
+index aaefc02..ea25545 100644
+--- a/bin/tests/system/nsupdate/clean.sh
++++ b/bin/tests/system/nsupdate/clean.sh
+@@ -32,6 +32,7 @@ rm -f ns3/example.db.jnl ns3/example.db
+ rm -f ns3/nsec3param.test.db.signed.jnl ns3/nsec3param.test.db ns3/nsec3param.test.db.signed ns3/dsset-nsec3param.test.
+ rm -f ns3/dnskey.test.db.signed.jnl ns3/dnskey.test.db ns3/dnskey.test.db.signed ns3/dsset-dnskey.test.
+ rm -f ns3/K*
++rm -f ns3/too-big.test.db
+ rm -f dig.out.*
+ rm -f jp.out.ns3.*
+ rm -f Kxxx.*
+diff --git a/bin/tests/system/nsupdate/ns3/named.conf b/bin/tests/system/nsupdate/ns3/named.conf
+index 2abd522..68ff27a 100644
+--- a/bin/tests/system/nsupdate/ns3/named.conf
++++ b/bin/tests/system/nsupdate/ns3/named.conf
+@@ -60,3 +60,10 @@ zone "dnskey.test" {
+ 	allow-update { any; };
+ 	file "dnskey.test.db.signed";
+ };
++
++zone "too-big.test" {
++	type master;
++	allow-update { any; };
++	max-records 3;
++	file "too-big.test.db";
++};
+diff --git a/bin/tests/system/nsupdate/ns3/too-big.test.db.in b/bin/tests/system/nsupdate/ns3/too-big.test.db.in
+new file mode 100644
+index 0000000..7ff1e4a
+--- /dev/null
++++ b/bin/tests/system/nsupdate/ns3/too-big.test.db.in
+@@ -0,0 +1,10 @@
++; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
++;
++; This Source Code Form is subject to the terms of the Mozilla Public
++; License, v. 2.0. If a copy of the MPL was not distributed with this
++; file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++$TTL 10
++too-big.test. IN SOA too-big.test. hostmaster.too-big.test. 1 3600 900 2419200 3600
++too-big.test. IN NS too-big.test.
++too-big.test. IN A 10.53.0.3
+diff --git a/bin/tests/system/nsupdate/setup.sh b/bin/tests/system/nsupdate/setup.sh
+index 828255e..43c4094 100644
+--- a/bin/tests/system/nsupdate/setup.sh
++++ b/bin/tests/system/nsupdate/setup.sh
+@@ -27,12 +27,14 @@ test -r $RANDFILE || $GENRANDOM 400 $RANDFILE
+ rm -f ns1/*.jnl ns1/example.db ns2/*.jnl ns2/example.bk
+ rm -f ns2/update.bk ns2/update.alt.bk
+ rm -f ns3/example.db.jnl
++rm -f ns3/too-big.test.db.jnl
+ 
+ cp -f ns1/example1.db ns1/example.db
+ sed 's/example.nil/other.nil/g' ns1/example1.db > ns1/other.db
+ sed 's/example.nil/unixtime.nil/g' ns1/example1.db > ns1/unixtime.db
+ sed 's/example.nil/keytests.nil/g' ns1/example1.db > ns1/keytests.db
+ cp -f ns3/example.db.in ns3/example.db
++cp -f ns3/too-big.test.db.in ns3/too-big.test.db
+ 
+ # update_test.pl has its own zone file because it
+ # requires a specific NS record set.
+diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh
+index 78d501e..0a6bbd3 100755
+--- a/bin/tests/system/nsupdate/tests.sh
++++ b/bin/tests/system/nsupdate/tests.sh
+@@ -581,5 +581,20 @@ if [ $ret -ne 0 ]; then
+     status=1
+ fi
+ 
++n=`expr $n + 1`
++echo "I:check that adding too many records is blocked ($n)"
++ret=0
++$NSUPDATE -v << EOF > nsupdate.out-$n 2>&1 && ret=1
++server 10.53.0.3 5300
++zone too-big.test.
++update add r1.too-big.test 3600 IN TXT r1.too-big.test
++send
++EOF
++grep "update failed: SERVFAIL" nsupdate.out-$n > /dev/null || ret=1
++DIG +tcp @10.53.0.3 -p 5300 r1.too-big.test TXT > dig.out.ns3.test$n
++grep "status: NXDOMAIN" dig.out.ns3.test$n > /dev/null || ret=1
++grep "records in zone (4) exceeds max-records (3)" ns3/named.run > /dev/null || ret=1
++[ $ret = 0 ] || { echo I:failed; status=1; }
++
+ echo "I:exit status: $status"
+ exit $status
+diff --git a/bin/tests/system/xfer/clean.sh b/bin/tests/system/xfer/clean.sh
+index 48aa159..da62a33 100644
+--- a/bin/tests/system/xfer/clean.sh
++++ b/bin/tests/system/xfer/clean.sh
+@@ -36,3 +36,4 @@ rm -f ns7/*.db ns7/*.bk ns7/*.jnl
+ rm -f */named.memstats
+ rm -f */named.run
+ rm -f */ans.run
++rm -f ns1/ixfr-too-big.db ns1/ixfr-too-big.db.jnl
+diff --git a/bin/tests/system/xfer/ns1/axfr-too-big.db b/bin/tests/system/xfer/ns1/axfr-too-big.db
+new file mode 100644
+index 0000000..d43760d
+--- /dev/null
++++ b/bin/tests/system/xfer/ns1/axfr-too-big.db
+@@ -0,0 +1,10 @@
++; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
++;
++; This Source Code Form is subject to the terms of the Mozilla Public
++; License, v. 2.0. If a copy of the MPL was not distributed with this
++; file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++$TTL	3600
++@	IN	SOA	. . 0 0 0 0 0
++@	IN	NS	.
++$GENERATE 1-29	host$	A	1.2.3.$
+diff --git a/bin/tests/system/xfer/ns1/ixfr-too-big.db.in b/bin/tests/system/xfer/ns1/ixfr-too-big.db.in
+new file mode 100644
+index 0000000..318bb77
+--- /dev/null
++++ b/bin/tests/system/xfer/ns1/ixfr-too-big.db.in
+@@ -0,0 +1,13 @@
++; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
++;
++; This Source Code Form is subject to the terms of the Mozilla Public
++; License, v. 2.0. If a copy of the MPL was not distributed with this
++; file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++$TTL	3600
++@	IN	SOA	. . 0 0 0 0 0
++@	IN	NS	ns1
++@	IN	NS	ns6
++ns1	IN	A	10.53.0.1
++ns6	IN	A	10.53.0.6
++$GENERATE 1-25	host$	A	1.2.3.$
+diff --git a/bin/tests/system/xfer/ns1/named.conf b/bin/tests/system/xfer/ns1/named.conf
+index 07dad85..1d29292 100644
+--- a/bin/tests/system/xfer/ns1/named.conf
++++ b/bin/tests/system/xfer/ns1/named.conf
+@@ -44,3 +44,14 @@ zone "slave" {
+ 	type master;
+ 	file "slave.db";
+ };
++
++zone "axfr-too-big" {
++        type master;
++        file "axfr-too-big.db";
++};
++
++zone "ixfr-too-big" {
++        type master;
++	allow-update { any; };
++        file "ixfr-too-big.db";
++};
+diff --git a/bin/tests/system/xfer/ns6/named.conf b/bin/tests/system/xfer/ns6/named.conf
+index c9421b1..a12a92c 100644
+--- a/bin/tests/system/xfer/ns6/named.conf
++++ b/bin/tests/system/xfer/ns6/named.conf
+@@ -52,3 +52,17 @@ zone "slave" {
+ 	masters { 10.53.0.1; };
+ 	file "slave.bk";
+ };
++
++zone "axfr-too-big" {
++	type slave;
++	max-records 30;
++	masters { 10.53.0.1; };
++	file "axfr-too-big.bk";
++};
++
++zone "ixfr-too-big" {
++	type slave;
++	max-records 30;
++	masters { 10.53.0.1; };
++	file "ixfr-too-big.bk";
++};
+diff --git a/bin/tests/system/xfer/setup.sh b/bin/tests/system/xfer/setup.sh
+index 56ca901..c55abf8 100644
+--- a/bin/tests/system/xfer/setup.sh
++++ b/bin/tests/system/xfer/setup.sh
+@@ -33,3 +33,5 @@ cp -f ns4/named.conf.base ns4/named.conf
+ 
+ cp ns2/slave.db.in ns2/slave.db
+ touch -t 200101010000 ns2/slave.db
++
++cp -f ns1/ixfr-too-big.db.in ns1/ixfr-too-big.db
+diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh
+index 67b2a1a..fe33f0a 100644
+--- a/bin/tests/system/xfer/tests.sh
++++ b/bin/tests/system/xfer/tests.sh
+@@ -368,5 +368,31 @@ $DIGCMD nil. TXT | grep 'incorrect key AXFR' >/dev/null && {
+     status=1
+ }
+ 
++n=`expr $n + 1`
++echo "I:test that a zone with too many records is rejected (AXFR) ($n)"
++tmp=0
++grep "'axfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null || tmp=1
++if test $tmp != 0 ; then echo "I:failed"; fi
++status=`expr $status + $tmp`
++
++n=`expr $n + 1`
++echo "I:test that a zone with too many records is rejected (IXFR) ($n)"
++tmp=0
++grep "'ixfr-too-big./IN.*: too many records" ns6/named.run >/dev/null && tmp=1
++$NSUPDATE << EOF
++zone ixfr-too-big
++server 10.53.0.1 5300
++update add the-31st-record.ixfr-too-big 0 TXT this is it
++send
++EOF
++for i in 1 2 3 4 5 6 7 8
++do
++    grep "'ixfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null && break
++    sleep 1
++done
++grep "'ixfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null || tmp=1
++if test $tmp != 0 ; then echo "I:failed"; fi
++status=`expr $status + $tmp`
++
+ echo "I:exit status: $status"
+ exit $status
+diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
+index 848b582..0369505 100644
+--- a/doc/arm/Bv9ARM-book.xml
++++ b/doc/arm/Bv9ARM-book.xml
+@@ -4858,6 +4858,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
+     <optional> use-queryport-pool <replaceable>yes_or_no</replaceable>; </optional>
+     <optional> queryport-pool-ports <replaceable>number</replaceable>; </optional>
+     <optional> queryport-pool-updateinterval <replaceable>number</replaceable>; </optional>
++    <optional> max-records <replaceable>number</replaceable>; </optional>
+     <optional> max-transfer-time-in <replaceable>number</replaceable>; </optional>
+     <optional> max-transfer-time-out <replaceable>number</replaceable>; </optional>
+     <optional> max-transfer-idle-in <replaceable>number</replaceable>; </optional>
+@@ -8164,6 +8165,16 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
+ 	    </varlistentry>
+ 
+ 	    <varlistentry>
++	      <term><command>max-records</command></term>
++	      <listitem>
++		<para>
++		  The maximum number of records permitted in a zone.
++		  The default is zero which means unlimited.
++	 	</para>
++	      </listitem>
++	    </varlistentry>
++
++	    <varlistentry>
+ 	      <term><command>host-statistics-max</command></term>
+ 	      <listitem>
+ 		<para>
+@@ -12056,6 +12067,16 @@ zone <replaceable>zone_name</replaceable> <optional><replaceable>class</replacea
+ 	      </varlistentry>
+ 
+ 	      <varlistentry>
++		<term><command>max-records</command></term>
++		<listitem>
++		  <para>
++		    See the description of
++		    <command>max-records</command> in <xref linkend="server_resource_limits"/>.
++		  </para>
++		</listitem>
++	      </varlistentry>
++
++	      <varlistentry>
+ 		<term><command>max-transfer-time-in</command></term>
+ 		<listitem>
+ 		  <para>
+diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
+index 095eb5b..36495e7 100644
+--- a/doc/arm/notes.xml
++++ b/doc/arm/notes.xml
+@@ -52,6 +52,15 @@
+     <itemizedlist>
+       <listitem>
+        <para>
++	  Added the ability to specify the maximum number of records
++	  permitted in a zone (max-records #;).  This provides a mechanism
++	  to block overly large zone transfers, which is a potential risk
++	  with slave zones from other parties, as described in CVE-2016-6170.
++	  [RT #42143]
++	</para>
++      </listitem>
++      <listitem>
++	<para>
+          Duplicate EDNS COOKIE options in a response could trigger
+          an assertion failure. This flaw is disclosed in CVE-2016-2088.
+          [RT #41809]
+diff --git a/lib/bind9/check.c b/lib/bind9/check.c
+index b8c05dd..edb7534 100644
+--- a/lib/bind9/check.c
++++ b/lib/bind9/check.c
+@@ -1510,6 +1510,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
+ 	  REDIRECTZONE },
+ 	{ "masters", SLAVEZONE | STUBZONE | REDIRECTZONE },
+ 	{ "max-ixfr-log-size", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
++	{ "max-records", MASTERZONE | SLAVEZONE | STUBZONE | STREDIRECTZONE |
++          STATICSTUBZONE | REDIRECTZONE },
+ 	{ "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
+ 	{ "max-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
+ 	{ "max-transfer-idle-in", SLAVEZONE | STUBZONE | STREDIRECTZONE },
+diff --git a/lib/dns/db.c b/lib/dns/db.c
+index 7e4f357..ced94a5 100644
+--- a/lib/dns/db.c
++++ b/lib/dns/db.c
+@@ -999,6 +999,19 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+ }
+ 
+ isc_result_t
++dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records,
++	       isc_uint64_t *bytes)
++{
++	REQUIRE(DNS_DB_VALID(db));
++	REQUIRE(dns_db_iszone(db) == ISC_TRUE);
++
++	if (db->methods->getsize != NULL)
++		return ((db->methods->getsize)(db, version, records, bytes));
++
++	return (ISC_R_NOTFOUND);
++}
++
++isc_result_t
+ dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ 		      isc_stdtime_t resign)
+ {
+diff --git a/lib/dns/ecdb.c b/lib/dns/ecdb.c
+index 553a339..b5d04d2 100644
+--- a/lib/dns/ecdb.c
++++ b/lib/dns/ecdb.c
+@@ -587,7 +587,8 @@ static dns_dbmethods_t ecdb_methods = {
+ 	NULL,			/* findnodeext */
+ 	NULL,			/* findext */
+ 	NULL,			/* setcachestats */
+-	NULL			/* hashsize */
++	NULL,			/* hashsize */
++	NULL			/* getsize */
+ };
+ 
+ static isc_result_t
+diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h
+index a4a4482..aff42d6 100644
+--- a/lib/dns/include/dns/db.h
++++ b/lib/dns/include/dns/db.h
+@@ -195,6 +195,8 @@ typedef struct dns_dbmethods {
+ 				   dns_rdataset_t *sigrdataset);
+ 	isc_result_t	(*setcachestats)(dns_db_t *db, isc_stats_t *stats);
+ 	unsigned int	(*hashsize)(dns_db_t *db);
++	isc_result_t	(*getsize)(dns_db_t *db, dns_dbversion_t *version,
++				   isc_uint64_t *records, isc_uint64_t *bytes);
+ } dns_dbmethods_t;
+ 
+ typedef isc_result_t
+@@ -1485,6 +1487,24 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+  */
+ 
+ isc_result_t
++dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records,
++               isc_uint64_t *bytes);
++/*%<
++ * Get the number of records in the given version of the database as well
++ * as the number bytes used to store those records.
++ *
++ * Requires:
++ * \li	'db' is a valid zone database.
++ * \li	'version' is NULL or a valid version.
++ * \li	'records' is NULL or a pointer to return the record count in.
++ * \li	'bytes' is NULL or a pointer to return the byte count in.
++ *
++ * Returns:
++ * \li	#ISC_R_SUCCESS
++ * \li	#ISC_R_NOTIMPLEMENTED
++ */
++
++isc_result_t
+ dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+ 		     isc_boolean_t create, dns_dbnode_t **nodep);
+ /*%<
+diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h
+index 3ac44b8..2e1e759 100644
+--- a/lib/dns/include/dns/rdataslab.h
++++ b/lib/dns/include/dns/rdataslab.h
+@@ -104,6 +104,7 @@ dns_rdataslab_tordataset(unsigned char *slab, unsigned int reservelen,
+  * Ensures:
+  *\li	'rdataset' is associated and points to a valid rdataest.
+  */
++
+ unsigned int
+ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen);
+ /*%<
+@@ -116,6 +117,18 @@ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen);
+  *\li	The number of bytes in the slab, including the reservelen.
+  */
+ 
++unsigned int
++dns_rdataslab_count(unsigned char *slab, unsigned int reservelen);
++/*%<
++ * Return the number of records in the rdataslab
++ *
++ * Requires:
++ *\li	'slab' points to a slab.
++ *
++ * Returns:
++ *\li	The number of records in the slab.
++ */
++
+ isc_result_t
+ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab,
+ 		    unsigned int reservelen, isc_mem_t *mctx,
+diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h
+index 7d11c2b..93d1fd5 100644
+--- a/lib/dns/include/dns/result.h
++++ b/lib/dns/include/dns/result.h
+@@ -157,8 +157,12 @@
+ #define DNS_R_BADCDS			(ISC_RESULTCLASS_DNS + 111)
+ #define DNS_R_BADCDNSKEY		(ISC_RESULTCLASS_DNS + 112)
+ #define DNS_R_OPTERR			(ISC_RESULTCLASS_DNS + 113)
++#define DNS_R_BADDNSTAP			(ISC_RESULTCLASS_DNS + 114)
++#define DNS_R_BADTSIG			(ISC_RESULTCLASS_DNS + 115)
++#define DNS_R_BADSIG0			(ISC_RESULTCLASS_DNS + 116)
++#define DNS_R_TOOMANYRECORDS		(ISC_RESULTCLASS_DNS + 117)
+ 
+-#define DNS_R_NRESULTS			114	/*%< Number of results */
++#define DNS_R_NRESULTS			118	/*%< Number of results */
+ 
+ /*
+  * DNS wire format rcodes.
+diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
+index a9367f1..227540b 100644
+--- a/lib/dns/include/dns/zone.h
++++ b/lib/dns/include/dns/zone.h
+@@ -296,6 +296,32 @@ dns_zone_getfile(dns_zone_t *zone);
+  */
+ 
+ void
++dns_zone_setmaxrecords(dns_zone_t *zone, isc_uint32_t records);
++/*%<
++ * 	Sets the maximim number of records permitted in a zone.
++ *	0 implies unlimited.
++ *
++ * Requires:
++ *\li	'zone' to be valid initialised zone.
++ *
++ * Returns:
++ *\li	void
++ */
++
++isc_uint32_t
++dns_zone_getmaxrecords(dns_zone_t *zone);
++/*%<
++ * 	Gets the maximim number of records permitted in a zone.
++ *	0 implies unlimited.
++ *
++ * Requires:
++ *\li	'zone' to be valid initialised zone.
++ *
++ * Returns:
++ *\li	isc_uint32_t maxrecords.
++ */
++
++void
+ dns_zone_setmaxttl(dns_zone_t *zone, isc_uint32_t maxttl);
+ /*%<
+  * 	Sets the max ttl of the zone.
+@@ -316,7 +342,7 @@ dns_zone_getmaxttl(dns_zone_t *zone);
+  *\li	'zone' to be valid initialised zone.
+  *
+  * Returns:
+- *\li	isc_uint32_t maxttl.
++ *\li	dns_ttl_t maxttl.
+  */
+ 
+ isc_result_t
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 62becfc..72d722f 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -209,6 +209,7 @@ typedef isc_uint64_t                    rbtdb_serial_t;
+ #define free_rbtdb_callback free_rbtdb_callback64
+ #define free_rdataset free_rdataset64
+ #define getnsec3parameters getnsec3parameters64
++#define getsize getsize64
+ #define getoriginnode getoriginnode64
+ #define getrrsetstats getrrsetstats64
+ #define getsigningtime getsigningtime64
+@@ -589,6 +590,13 @@ typedef struct rbtdb_version {
+ 	isc_uint16_t			iterations;
+ 	isc_uint8_t			salt_length;
+ 	unsigned char			salt[DNS_NSEC3_SALTSIZE];
++
++	/*
++	 * records and bytes are covered by rwlock.
++	 */
++	isc_rwlock_t                    rwlock;
++	isc_uint64_t			records;
++	isc_uint64_t			bytes;
+ } rbtdb_version_t;
+ 
+ typedef ISC_LIST(rbtdb_version_t)       rbtdb_versionlist_t;
+@@ -1130,6 +1138,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
+ 		INSIST(refs == 0);
+ 		UNLINK(rbtdb->open_versions, rbtdb->current_version, link);
+ 		isc_refcount_destroy(&rbtdb->current_version->references);
++		isc_rwlock_destroy(&rbtdb->current_version->rwlock);
+ 		isc_mem_put(rbtdb->common.mctx, rbtdb->current_version,
+ 			    sizeof(rbtdb_version_t));
+ 	}
+@@ -1383,6 +1392,7 @@ allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial,
+ 
+ static isc_result_t
+ newversion(dns_db_t *db, dns_dbversion_t **versionp) {
++	isc_result_t result;
+ 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ 	rbtdb_version_t *version;
+ 
+@@ -1415,13 +1425,28 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ 			version->salt_length = 0;
+ 			memset(version->salt, 0, sizeof(version->salt));
+ 		}
+-		rbtdb->next_serial++;
+-		rbtdb->future_version = version;
+-	}
++		result = isc_rwlock_init(&version->rwlock, 0, 0);
++		if (result != ISC_R_SUCCESS) {
++			isc_refcount_destroy(&version->references);
++			isc_mem_put(rbtdb->common.mctx, version,
++				    sizeof(*version));
++			version = NULL;
++		} else {
++			RWLOCK(&rbtdb->current_version->rwlock,
++			       isc_rwlocktype_read);
++			version->records = rbtdb->current_version->records;
++			version->bytes = rbtdb->current_version->bytes;
++			RWUNLOCK(&rbtdb->current_version->rwlock,
++				 isc_rwlocktype_read);
++			rbtdb->next_serial++;
++			rbtdb->future_version = version;
++		}
++	} else
++		result = ISC_R_NOMEMORY;
+ 	RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+ 
+ 	if (version == NULL)
+-		return (ISC_R_NOMEMORY);
++		return (result);
+ 
+ 	*versionp = version;
+ 
+@@ -2681,6 +2706,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
+ 
+ 	if (cleanup_version != NULL) {
+ 		INSIST(EMPTY(cleanup_version->changed_list));
++		isc_rwlock_destroy(&cleanup_version->rwlock);
+ 		isc_mem_put(rbtdb->common.mctx, cleanup_version,
+ 			    sizeof(*cleanup_version));
+ 	}
+@@ -6254,6 +6280,26 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ 		else
+ 			rbtnode->data = newheader;
+ 		newheader->next = topheader->next;
++		if (rbtversion != NULL)
++			RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
++		if (rbtversion != NULL && !header_nx) {
++			rbtversion->records -=
++				dns_rdataslab_count((unsigned char *)header,
++						    sizeof(*header));
++			rbtversion->bytes -=
++				dns_rdataslab_size((unsigned char *)header,
++						   sizeof(*header));
++		}
++		if (rbtversion != NULL && !newheader_nx) {
++			rbtversion->records +=
++				dns_rdataslab_count((unsigned char *)newheader,
++						    sizeof(*newheader));
++			rbtversion->bytes +=
++				dns_rdataslab_size((unsigned char *)newheader,
++						   sizeof(*newheader));
++		}
++		if (rbtversion != NULL)
++			RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
+ 		if (loading) {
+ 			/*
+ 			 * There are no other references to 'header' when
+@@ -6355,6 +6401,16 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ 			newheader->down = NULL;
+ 			rbtnode->data = newheader;
+ 		}
++		if (rbtversion != NULL && !newheader_nx) {
++			RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
++			rbtversion->records +=
++				dns_rdataslab_count((unsigned char *)newheader,
++						    sizeof(*newheader));
++			rbtversion->bytes +=
++				dns_rdataslab_size((unsigned char *)newheader,
++						   sizeof(*newheader));
++			RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
++		}
+ 		idx = newheader->node->locknum;
+ 		if (IS_CACHE(rbtdb)) {
+ 			ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+@@ -6811,6 +6867,12 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ 			 */
+ 			newheader->additional_auth = NULL;
+ 			newheader->additional_glue = NULL;
++			rbtversion->records +=
++				dns_rdataslab_count((unsigned char *)newheader,
++						    sizeof(*newheader));
++			rbtversion->bytes +=
++				dns_rdataslab_size((unsigned char *)newheader,
++						   sizeof(*newheader));
+ 		} else if (result == DNS_R_NXRRSET) {
+ 			/*
+ 			 * This subtraction would remove all of the rdata;
+@@ -6846,6 +6908,12 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ 		 * topheader.
+ 		 */
+ 		INSIST(rbtversion->serial >= topheader->serial);
++		rbtversion->records -=
++				dns_rdataslab_count((unsigned char *)header,
++						    sizeof(*header));
++		rbtversion->bytes -=
++				dns_rdataslab_size((unsigned char *)header,
++						   sizeof(*header));
+ 		if (topheader_prev != NULL)
+ 			topheader_prev->next = newheader;
+ 		else
+@@ -7172,6 +7240,7 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize,
+ 	unsigned char *limit = ((unsigned char *) base) + filesize;
+ 	unsigned char *p;
+ 	size_t size;
++	unsigned int count;
+ 
+ 	REQUIRE(rbtnode != NULL);
+ 
+@@ -7179,6 +7248,9 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize,
+ 		p = (unsigned char *) header;
+ 
+ 		size = dns_rdataslab_size(p, sizeof(*header));
++		count = dns_rdataslab_count(p, sizeof(*header));;
++		rbtdb->current_version->records += count;
++		rbtdb->current_version->bytes += size;
+ 		isc_crc64_update(crc, p, size);
+ #ifdef DEBUG
+ 		hexdump("hashing header", p, sizeof(rdatasetheader_t));
+@@ -7777,6 +7849,33 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
+ }
+ 
+ static isc_result_t
++getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records,
++        isc_uint64_t *bytes)
++{
++	dns_rbtdb_t *rbtdb;
++	isc_result_t result = ISC_R_SUCCESS;
++	rbtdb_version_t *rbtversion = version;
++
++	rbtdb = (dns_rbtdb_t *)db;
++
++	REQUIRE(VALID_RBTDB(rbtdb));
++	INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
++
++	if (rbtversion == NULL)
++		rbtversion = rbtdb->current_version;
++
++	RWLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
++	if (records != NULL)
++		*records = rbtversion->records;
++
++	if (bytes != NULL)
++		*bytes = rbtversion->bytes;
++	RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
++
++	return (result);
++}
++
++static isc_result_t
+ setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
+ 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ 	isc_stdtime_t oldresign;
+@@ -7972,7 +8071,8 @@ static dns_dbmethods_t zone_methods = {
+ 	NULL,
+ 	NULL,
+ 	NULL,
+-	hashsize
++	hashsize,
++	getsize
+ };
+ 
+ static dns_dbmethods_t cache_methods = {
+@@ -8018,7 +8118,8 @@ static dns_dbmethods_t cache_methods = {
+ 	NULL,
+ 	NULL,
+ 	setcachestats,
+-	hashsize
++	hashsize,
++	NULL
+ };
+ 
+ isc_result_t
+@@ -8310,6 +8411,20 @@ dns_rbtdb_create
+ 	rbtdb->current_version->salt_length = 0;
+ 	memset(rbtdb->current_version->salt, 0,
+ 	       sizeof(rbtdb->current_version->salt));
++	result = isc_rwlock_init(&rbtdb->current_version->rwlock, 0, 0);
++	if (result != ISC_R_SUCCESS) {
++		isc_refcount_destroy(&rbtdb->current_version->references);
++		isc_mem_put(mctx, rbtdb->current_version,
++			    sizeof(*rbtdb->current_version));
++		rbtdb->current_version = NULL;
++		isc_refcount_decrement(&rbtdb->references, NULL);
++		isc_refcount_destroy(&rbtdb->references);
++		free_rbtdb(rbtdb, ISC_FALSE, NULL);
++		return (result);
++	}
++
++	rbtdb->current_version->records = 0;
++	rbtdb->current_version->bytes = 0;
+ 	rbtdb->future_version = NULL;
+ 	ISC_LIST_INIT(rbtdb->open_versions);
+ 	/*
+diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c
+index e29dc84..63e3728 100644
+--- a/lib/dns/rdataslab.c
++++ b/lib/dns/rdataslab.c
+@@ -523,6 +523,19 @@ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen) {
+ 	return ((unsigned int)(current - slab));
+ }
+ 
++unsigned int
++dns_rdataslab_count(unsigned char *slab, unsigned int reservelen) {
++	unsigned int count;
++	unsigned char *current;
++
++	REQUIRE(slab != NULL);
++
++	current = slab + reservelen;
++	count = *current++ * 256;
++	count += *current++;
++	return (count);
++}
++
+ /*
+  * Make the dns_rdata_t 'rdata' refer to the slab item
+  * beginning at '*current', which is part of a slab of type
+diff --git a/lib/dns/result.c b/lib/dns/result.c
+index 7be4f57..a621909 100644
+--- a/lib/dns/result.c
++++ b/lib/dns/result.c
+@@ -167,11 +167,16 @@ static const char *text[DNS_R_NRESULTS] = {
+ 	"covered by negative trust anchor",    /*%< 110 DNS_R_NTACOVERED */
+ 	"bad CDS",			       /*%< 111 DNS_R_BADCSD */
+ 	"bad CDNSKEY",			       /*%< 112 DNS_R_BADCDNSKEY */
+-	"malformed OPT option"		       /*%< 113 DNS_R_OPTERR */
++	"malformed OPT option",		       /*%< 113 DNS_R_OPTERR */
++	"malformed DNSTAP data",	       /*%< 114 DNS_R_BADDNSTAP */
++
++	"TSIG in wrong location",	       /*%< 115 DNS_R_BADTSIG */
++	"SIG(0) in wrong location",	       /*%< 116 DNS_R_BADSIG0 */
++	"too many records",	               /*%< 117 DNS_R_TOOMANYRECORDS */
+ };
+ 
+ static const char *rcode_text[DNS_R_NRCODERESULTS] = {
+-	"NOERROR",				/*%< 0 DNS_R_NOEROR */
++	"NOERROR",				/*%< 0 DNS_R_NOERROR */
+ 	"FORMERR",				/*%< 1 DNS_R_FORMERR */
+ 	"SERVFAIL",				/*%< 2 DNS_R_SERVFAIL */
+ 	"NXDOMAIN",				/*%< 3 DNS_R_NXDOMAIN */
+diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c
+index abfeeb0..19397e0 100644
+--- a/lib/dns/sdb.c
++++ b/lib/dns/sdb.c
+@@ -1298,7 +1298,8 @@ static dns_dbmethods_t sdb_methods = {
+ 	findnodeext,
+ 	findext,
+ 	NULL,			/* setcachestats */
+-	NULL			/* hashsize */
++	NULL,			/* hashsize */
++	NULL			/* getsize */
+ };
+ 
+ static isc_result_t
+diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c
+index b1198a4..0e3163d 100644
+--- a/lib/dns/sdlz.c
++++ b/lib/dns/sdlz.c
+@@ -1269,7 +1269,8 @@ static dns_dbmethods_t sdlzdb_methods = {
+ 	findnodeext,
+ 	findext,
+ 	NULL,			/* setcachestats */
+-	NULL			/* hashsize */
++	NULL,			/* hashsize */
++	NULL			/* getsize */
+ };
+ 
+ /*
+diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
+index 2a6c1b4..ac566e1 100644
+--- a/lib/dns/xfrin.c
++++ b/lib/dns/xfrin.c
+@@ -149,6 +149,9 @@ struct dns_xfrin_ctx {
+ 	unsigned int		nrecs;		/*%< Number of records recvd */
+ 	isc_uint64_t		nbytes;		/*%< Number of bytes received */
+ 
++	unsigned int		maxrecords;	/*%< The maximum number of
++						     records set for the zone */
++
+ 	isc_time_t		start;		/*%< Start time of the transfer */
+ 	isc_time_t		end;		/*%< End time of the transfer */
+ 
+@@ -309,10 +312,18 @@ axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ static isc_result_t
+ axfr_apply(dns_xfrin_ctx_t *xfr) {
+ 	isc_result_t result;
++	isc_uint64_t records;
+ 
+ 	CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add, xfr->axfr.add_private));
+ 	xfr->difflen = 0;
+ 	dns_diff_clear(&xfr->diff);
++	if (xfr->maxrecords != 0U) {
++		result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
++		if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
++			result = DNS_R_TOOMANYRECORDS;
++			goto failure;
++		}
++	}
+ 	result = ISC_R_SUCCESS;
+  failure:
+ 	return (result);
+@@ -396,6 +407,7 @@ ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ static isc_result_t
+ ixfr_apply(dns_xfrin_ctx_t *xfr) {
+ 	isc_result_t result;
++	isc_uint64_t records;
+ 
+ 	if (xfr->ver == NULL) {
+ 		CHECK(dns_db_newversion(xfr->db, &xfr->ver));
+@@ -403,6 +415,13 @@ ixfr_apply(dns_xfrin_ctx_t *xfr) {
+ 			CHECK(dns_journal_begin_transaction(xfr->ixfr.journal));
+ 	}
+ 	CHECK(dns_diff_apply(&xfr->diff, xfr->db, xfr->ver));
++	if (xfr->maxrecords != 0U) {
++		result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
++		if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
++			result = DNS_R_TOOMANYRECORDS;
++			goto failure;
++		}
++	}
+ 	if (xfr->ixfr.journal != NULL) {
+ 		result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff);
+ 		if (result != ISC_R_SUCCESS)
+@@ -759,7 +778,7 @@ xfrin_reset(dns_xfrin_ctx_t *xfr) {
+ 
+ static void
+ xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) {
+-	if (result != DNS_R_UPTODATE) {
++	if (result != DNS_R_UPTODATE && result != DNS_R_TOOMANYRECORDS) {
+ 		xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s",
+ 			  msg, isc_result_totext(result));
+ 		if (xfr->is_ixfr)
+@@ -852,6 +871,7 @@ xfrin_create(isc_mem_t *mctx,
+ 	xfr->nmsg = 0;
+ 	xfr->nrecs = 0;
+ 	xfr->nbytes = 0;
++	xfr->maxrecords = dns_zone_getmaxrecords(zone);
+ 	isc_time_now(&xfr->start);
+ 
+ 	xfr->tsigkey = NULL;
+diff --git a/lib/dns/zone.c b/lib/dns/zone.c
+index 90e558d..2b0d8e4 100644
+--- a/lib/dns/zone.c
++++ b/lib/dns/zone.c
+@@ -253,6 +253,8 @@ struct dns_zone {
+ 	isc_uint32_t		maxretry;
+ 	isc_uint32_t		minretry;
+ 
++	isc_uint32_t		maxrecords;
++
+ 	isc_sockaddr_t		*masters;
+ 	isc_dscp_t		*masterdscps;
+ 	dns_name_t		**masterkeynames;
+@@ -10088,6 +10090,20 @@ dns_zone_setmaxretrytime(dns_zone_t *zone, isc_uint32_t val) {
+ 	zone->maxretry = val;
+ }
+ 
++isc_uint32_t
++dns_zone_getmaxrecords(dns_zone_t *zone) {
++        REQUIRE(DNS_ZONE_VALID(zone));
++
++	return (zone->maxrecords);
++}
++
++void
++dns_zone_setmaxrecords(dns_zone_t *zone, isc_uint32_t val) {
++        REQUIRE(DNS_ZONE_VALID(zone));
++
++	zone->maxrecords = val;
++}
++
+ static isc_boolean_t
+ notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name,
+ 		isc_sockaddr_t *addr, dns_tsigkey_t *key)
+@@ -14431,7 +14447,7 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
+ 	DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR);
+ 
+ 	TIME_NOW(&now);
+-	switch (result) {
++	switch (xfrresult) {
+ 	case ISC_R_SUCCESS:
+ 		DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ 		/*FALLTHROUGH*/
+@@ -14558,6 +14574,11 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
+ 		DNS_ZONE_SETFLAG(zone, DNS_ZONEFLAG_NOIXFR);
+ 		goto same_master;
+ 
++	case DNS_R_TOOMANYRECORDS:
++		DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
++		inc_stats(zone, dns_zonestatscounter_xfrfail);
++		break;
++
+ 	default:
+ 	next_master:
+ 		/*
+diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
+index 780ab46..e7ff1cc 100644
+--- a/lib/isccfg/namedconf.c
++++ b/lib/isccfg/namedconf.c
+@@ -1679,6 +1679,7 @@ zone_clauses[] = {
+ 	{ "masterfile-format", &cfg_type_masterformat, 0 },
+ 	{ "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
+ 	{ "max-journal-size", &cfg_type_sizenodefault, 0 },
++	{ "max-records", &cfg_type_uint32, 0 },
+ 	{ "max-refresh-time", &cfg_type_uint32, 0 },
+ 	{ "max-retry-time", &cfg_type_uint32, 0 },
+ 	{ "max-transfer-idle-in", &cfg_type_uint32, 0 },
+-- 
+2.7.4
+
diff --git a/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb b/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
index fa45809..8160625 100644
--- a/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
+++ b/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
@@ -28,6 +28,7 @@ SRC_URI = "ftp://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.gz \
            file://CVE-2016-2775.patch \
            file://CVE-2016-2776.patch \
            file://CVE-2016-8864.patch \
+           file://CVE-2016-6170.patch \
            "
 
 SRC_URI[md5sum] = "bcf7e772b616f7259420a3edc5df350a"
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* ✗ patchtest: failure for bind: 2 CVE fixes
  2017-04-13  5:20 [PATCH 0/2] bind: 2 CVE fixes Yi Zhao
  2017-04-13  5:20 ` [PATCH 1/2] bind: Security fix CVE-2016-8864 Yi Zhao
  2017-04-13  5:20 ` [PATCH 2/2] bind: Security fix CVE-2016-6170 Yi Zhao
@ 2017-04-13  5:32 ` Patchwork
  2 siblings, 0 replies; 4+ messages in thread
From: Patchwork @ 2017-04-13  5:32 UTC (permalink / raw)
  To: Yi Zhao; +Cc: openembedded-core

== Series Details ==

Series: bind: 2 CVE fixes
Revision: 1
URL   : https://patchwork.openembedded.org/series/6337/
State : failure

== Summary ==


Thank you for submitting this patch series to OpenEmbedded Core. This is
an automated response. Several tests have been executed on the proposed
series by patchtest resulting in the following failures:



* Patch            [1/2] bind: Security fix CVE-2016-8864
 Issue             Missing or incorrectly formatted CVE tag in included patch file [test_cve_tag_format] 
  Suggested fix    Correct or include the CVE tag on cve patch with format: "CVE: CVE-YYYY-XXXX"



If you believe any of these test results are incorrect, please reply to the
mailing list (openembedded-core@lists.openembedded.org) raising your concerns.
Otherwise we would appreciate you correcting the issues and submitting a new
version of the patchset if applicable. Please ensure you add/increment the
version number when sending the new version (i.e. [PATCH] -> [PATCH v2] ->
[PATCH v3] -> ...).

---
Test framework: http://git.yoctoproject.org/cgit/cgit.cgi/patchtest
Test suite:     http://git.yoctoproject.org/cgit/cgit.cgi/patchtest-oe



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-04-13  5:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-13  5:20 [PATCH 0/2] bind: 2 CVE fixes Yi Zhao
2017-04-13  5:20 ` [PATCH 1/2] bind: Security fix CVE-2016-8864 Yi Zhao
2017-04-13  5:20 ` [PATCH 2/2] bind: Security fix CVE-2016-6170 Yi Zhao
2017-04-13  5:32 ` ✗ patchtest: failure for bind: 2 CVE fixes Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.