From 56fc8c117dde2d7b6b68e617f682901526adc4ef Mon Sep 17 00:00:00 2001 From: Kai Kang Date: Fri, 22 Nov 2019 10:04:26 +0800 Subject: bind: fix CVE-2019-6471 and CVE-2018-5743 Backport patches to fix CVE-2019-6471 and CVE-2018-5743 for bind. CVE-2019-6471 is fixed by 0001-bind-fix-CVE-2019-6471.patch and the other 6 patches are for CVE-2018-5743. And backport one more patch to fix compile error on arm caused by these 6 commits. (From OE-Core rev: 3c39d4158677b97253df63f23b74c3a9dd5539f6) (From OE-Core rev: 230a96ddecf940a7caee9e9268b21aa5f65a7f14) Signed-off-by: Kai Kang Signed-off-by: Richard Purdie Signed-off-by: Anuj Mittal Signed-off-by: Armin Kuster Signed-off-by: Richard Purdie --- .../bind/bind/0001-bind-fix-CVE-2019-6471.patch | 64 ++ .../0001-fix-enforcement-of-tcp-clients-v1.patch | 60 ++ ...02-tcp-clients-could-still-be-exceeded-v2.patch | 670 +++++++++++++++ ...-reference-counter-for-pipeline-groups-v3.patch | 278 +++++++ ...uota-accounting-and-client-mortality-chec.patch | 512 ++++++++++++ ...pquota-and-pipeline-refs-allow-special-ca.patch | 911 +++++++++++++++++++++ ...tore-allowance-for-tcp-clients-interfaces.patch | 80 ++ ...mic-operations-in-bin-named-client.c-with.patch | 140 ++++ meta/recipes-connectivity/bind/bind_9.11.5-P4.bb | 8 + 9 files changed, 2723 insertions(+) create mode 100644 meta/recipes-connectivity/bind/bind/0001-bind-fix-CVE-2019-6471.patch create mode 100644 meta/recipes-connectivity/bind/bind/0001-fix-enforcement-of-tcp-clients-v1.patch create mode 100644 meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch create mode 100644 meta/recipes-connectivity/bind/bind/0003-use-reference-counter-for-pipeline-groups-v3.patch create mode 100644 meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch create mode 100644 meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch create mode 100644 meta/recipes-connectivity/bind/bind/0006-restore-allowance-for-tcp-clients-interfaces.patch create mode 100644 meta/recipes-connectivity/bind/bind/0007-Replace-atomic-operations-in-bin-named-client.c-with.patch diff --git a/meta/recipes-connectivity/bind/bind/0001-bind-fix-CVE-2019-6471.patch b/meta/recipes-connectivity/bind/bind/0001-bind-fix-CVE-2019-6471.patch new file mode 100644 index 0000000000..2fed99e1bb --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0001-bind-fix-CVE-2019-6471.patch @@ -0,0 +1,64 @@ +Backport patch to fix CVE-2019-6471. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2019-6471 + +CVE: CVE-2019-6471 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/3a9c7bb] + +Signed-off-by: Kai Kang + +From 3a9c7bb80d4a609b86427406d9dd783199920b5b Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Tue, 19 Mar 2019 14:14:21 +1100 +Subject: [PATCH] move item_out test inside lock in dns_dispatch_getnext() + +(cherry picked from commit 60c42f849d520564ed42e5ed0ba46b4b69c07712) +--- + lib/dns/dispatch.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c +index 408beda367..3278db4a07 100644 +--- a/lib/dns/dispatch.c ++++ b/lib/dns/dispatch.c +@@ -134,7 +134,7 @@ struct dns_dispentry { + isc_task_t *task; + isc_taskaction_t action; + void *arg; +- bool item_out; ++ bool item_out; + dispsocket_t *dispsocket; + ISC_LIST(dns_dispatchevent_t) items; + ISC_LINK(dns_dispentry_t) link; +@@ -3422,13 +3422,14 @@ dns_dispatch_getnext(dns_dispentry_t *resp, dns_dispatchevent_t **sockevent) { + disp = resp->disp; + REQUIRE(VALID_DISPATCH(disp)); + +- REQUIRE(resp->item_out == true); +- resp->item_out = false; +- + ev = *sockevent; + *sockevent = NULL; + + LOCK(&disp->lock); ++ ++ REQUIRE(resp->item_out == true); ++ resp->item_out = false; ++ + if (ev->buffer.base != NULL) + free_buffer(disp, ev->buffer.base, ev->buffer.length); + free_devent(disp, ev); +@@ -3573,6 +3574,9 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp, + isc_task_send(disp->task[0], &disp->ctlevent); + } + ++/* ++ * disp must be locked. ++ */ + static void + do_cancel(dns_dispatch_t *disp) { + dns_dispatchevent_t *ev; +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0001-fix-enforcement-of-tcp-clients-v1.patch b/meta/recipes-connectivity/bind/bind/0001-fix-enforcement-of-tcp-clients-v1.patch new file mode 100644 index 0000000000..48ae125f84 --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0001-fix-enforcement-of-tcp-clients-v1.patch @@ -0,0 +1,60 @@ +Backport patch to fix CVE-2018-5743. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2018-5743 + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/ec2d50d] + +Signed-off-by: Kai Kang + +From ec2d50da8d81814640e28593d912f4b96c7efece Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Witold=20Kr=C4=99cicki?= +Date: Thu, 3 Jan 2019 14:17:43 +0100 +Subject: [PATCH 1/6] fix enforcement of tcp-clients (v1) + +tcp-clients settings could be exceeded in some cases by +creating more and more active TCP clients that are over +the set quota limit, which in the end could lead to a +DoS attack by e.g. exhaustion of file descriptors. + +If TCP client we're closing went over the quota (so it's +not attached to a quota) mark it as mortal - so that it +will be destroyed and not set up to listen for new +connections - unless it's the last client for a specific +interface. + +(cherry picked from commit f97131d21b97381cef72b971b157345c1f9b4115) +(cherry picked from commit 9689ffc485df8f971f0ad81ab8ab1f5389493776) +--- + bin/named/client.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index d482da7121..0739dd48af 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -421,8 +421,19 @@ exit_check(ns_client_t *client) { + isc_socket_detach(&client->tcpsocket); + } + +- if (client->tcpquota != NULL) ++ if (client->tcpquota != NULL) { + isc_quota_detach(&client->tcpquota); ++ } else { ++ /* ++ * We went over quota with this client, we don't ++ * want to restart listening unless this is the ++ * last client on this interface, which is ++ * checked later. ++ */ ++ if (TCP_CLIENT(client)) { ++ client->mortal = true; ++ } ++ } + + if (client->timerset) { + (void)isc_timer_reset(client->timer, +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch b/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch new file mode 100644 index 0000000000..ca4e8b1a66 --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch @@ -0,0 +1,670 @@ +Backport patch to fix CVE-2018-5743. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2018-5743 + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/719f604] + +Signed-off-by: Kai Kang + +From 719f604e3fad5b7479bd14e2fa0ef4413f0a8fdc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Witold=20Kr=C4=99cicki?= +Date: Fri, 4 Jan 2019 12:50:51 +0100 +Subject: [PATCH 2/6] tcp-clients could still be exceeded (v2) + +the TCP client quota could still be ineffective under some +circumstances. this change: + +- improves quota accounting to ensure that TCP clients are + properly limited, while still guaranteeing that at least one client + is always available to serve TCP connections on each interface. +- uses more descriptive names and removes one (ntcptarget) that + was no longer needed +- adds comments + +(cherry picked from commit 924651f1d5e605cd186d03f4f7340bcc54d77cc2) +(cherry picked from commit 55a7a458e30e47874d34bdf1079eb863a0512396) +--- + bin/named/client.c | 311 ++++++++++++++++++++----- + bin/named/include/named/client.h | 14 +- + bin/named/include/named/interfacemgr.h | 11 +- + bin/named/interfacemgr.c | 8 +- + 4 files changed, 267 insertions(+), 77 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index 0739dd48af..a7b49a0f71 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -246,10 +246,11 @@ static void ns_client_dumpmessage(ns_client_t *client, const char *reason); + static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + dns_dispatch_t *disp, bool tcp); + static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, +- isc_socket_t *sock); ++ isc_socket_t *sock, ns_client_t *oldclient); + static inline bool +-allowed(isc_netaddr_t *addr, dns_name_t *signer, isc_netaddr_t *ecs_addr, +- uint8_t ecs_addrlen, uint8_t *ecs_scope, dns_acl_t *acl); ++allowed(isc_netaddr_t *addr, dns_name_t *signer, ++ isc_netaddr_t *ecs_addr, uint8_t ecs_addrlen, ++ uint8_t *ecs_scope, dns_acl_t *acl) + static void compute_cookie(ns_client_t *client, uint32_t when, + uint32_t nonce, const unsigned char *secret, + isc_buffer_t *buf); +@@ -405,8 +406,11 @@ exit_check(ns_client_t *client) { + */ + INSIST(client->recursionquota == NULL); + INSIST(client->newstate <= NS_CLIENTSTATE_READY); +- if (client->nreads > 0) ++ ++ if (client->nreads > 0) { + dns_tcpmsg_cancelread(&client->tcpmsg); ++ } ++ + if (client->nreads != 0) { + /* Still waiting for read cancel completion. */ + return (true); +@@ -416,25 +420,58 @@ exit_check(ns_client_t *client) { + dns_tcpmsg_invalidate(&client->tcpmsg); + client->tcpmsg_valid = false; + } ++ + if (client->tcpsocket != NULL) { + CTRACE("closetcp"); + isc_socket_detach(&client->tcpsocket); ++ ++ if (client->tcpactive) { ++ LOCK(&client->interface->lock); ++ INSIST(client->interface->ntcpactive > 0); ++ client->interface->ntcpactive--; ++ UNLOCK(&client->interface->lock); ++ client->tcpactive = false; ++ } + } + + if (client->tcpquota != NULL) { +- isc_quota_detach(&client->tcpquota); +- } else { + /* +- * We went over quota with this client, we don't +- * want to restart listening unless this is the +- * last client on this interface, which is +- * checked later. ++ * If we are not in a pipeline group, or ++ * we are the last client in the group, detach from ++ * tcpquota; otherwise, transfer the quota to ++ * another client in the same group. + */ +- if (TCP_CLIENT(client)) { +- client->mortal = true; ++ if (!ISC_LINK_LINKED(client, glink) || ++ (client->glink.next == NULL && ++ client->glink.prev == NULL)) ++ { ++ isc_quota_detach(&client->tcpquota); ++ } else if (client->glink.next != NULL) { ++ INSIST(client->glink.next->tcpquota == NULL); ++ client->glink.next->tcpquota = client->tcpquota; ++ client->tcpquota = NULL; ++ } else { ++ INSIST(client->glink.prev->tcpquota == NULL); ++ client->glink.prev->tcpquota = client->tcpquota; ++ client->tcpquota = NULL; + } + } + ++ /* ++ * Unlink from pipeline group. ++ */ ++ if (ISC_LINK_LINKED(client, glink)) { ++ if (client->glink.next != NULL) { ++ client->glink.next->glink.prev = ++ client->glink.prev; ++ } ++ if (client->glink.prev != NULL) { ++ client->glink.prev->glink.next = ++ client->glink.next; ++ } ++ ISC_LINK_INIT(client, glink); ++ } ++ + if (client->timerset) { + (void)isc_timer_reset(client->timer, + isc_timertype_inactive, +@@ -455,15 +492,16 @@ exit_check(ns_client_t *client) { + * that already. Check whether this client needs to remain + * active and force it to go inactive if not. + * +- * UDP clients go inactive at this point, but TCP clients +- * may remain active if we have fewer active TCP client +- * objects than desired due to an earlier quota exhaustion. ++ * UDP clients go inactive at this point, but a TCP client ++ * will needs to remain active if no other clients are ++ * listening for TCP requests on this interface, to ++ * prevent this interface from going nonresponsive. + */ + if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) { + LOCK(&client->interface->lock); +- if (client->interface->ntcpcurrent < +- client->interface->ntcptarget) ++ if (client->interface->ntcpaccepting == 0) { + client->mortal = false; ++ } + UNLOCK(&client->interface->lock); + } + +@@ -472,15 +510,17 @@ exit_check(ns_client_t *client) { + * queue for recycling. + */ + if (client->mortal) { +- if (client->newstate > NS_CLIENTSTATE_INACTIVE) ++ if (client->newstate > NS_CLIENTSTATE_INACTIVE) { + client->newstate = NS_CLIENTSTATE_INACTIVE; ++ } + } + + if (NS_CLIENTSTATE_READY == client->newstate) { + if (TCP_CLIENT(client)) { + client_accept(client); +- } else ++ } else { + client_udprecv(client); ++ } + client->newstate = NS_CLIENTSTATE_MAX; + return (true); + } +@@ -492,41 +532,57 @@ exit_check(ns_client_t *client) { + /* + * We are trying to enter the inactive state. + */ +- if (client->naccepts > 0) ++ if (client->naccepts > 0) { + isc_socket_cancel(client->tcplistener, client->task, + ISC_SOCKCANCEL_ACCEPT); ++ } + + /* Still waiting for accept cancel completion. */ +- if (! (client->naccepts == 0)) ++ if (! (client->naccepts == 0)) { + return (true); ++ } + + /* Accept cancel is complete. */ +- if (client->nrecvs > 0) ++ if (client->nrecvs > 0) { + isc_socket_cancel(client->udpsocket, client->task, + ISC_SOCKCANCEL_RECV); ++ } + + /* Still waiting for recv cancel completion. */ +- if (! (client->nrecvs == 0)) ++ if (! (client->nrecvs == 0)) { + return (true); ++ } + + /* Still waiting for control event to be delivered */ +- if (client->nctls > 0) ++ if (client->nctls > 0) { + return (true); +- +- /* Deactivate the client. */ +- if (client->interface) +- ns_interface_detach(&client->interface); ++ } + + INSIST(client->naccepts == 0); + INSIST(client->recursionquota == NULL); +- if (client->tcplistener != NULL) ++ if (client->tcplistener != NULL) { + isc_socket_detach(&client->tcplistener); + +- if (client->udpsocket != NULL) ++ if (client->tcpactive) { ++ LOCK(&client->interface->lock); ++ INSIST(client->interface->ntcpactive > 0); ++ client->interface->ntcpactive--; ++ UNLOCK(&client->interface->lock); ++ client->tcpactive = false; ++ } ++ } ++ if (client->udpsocket != NULL) { + isc_socket_detach(&client->udpsocket); ++ } + +- if (client->dispatch != NULL) ++ /* Deactivate the client. */ ++ if (client->interface != NULL) { ++ ns_interface_detach(&client->interface); ++ } ++ ++ if (client->dispatch != NULL) { + dns_dispatch_detach(&client->dispatch); ++ } + + client->attributes = 0; + client->mortal = false; +@@ -551,10 +607,13 @@ exit_check(ns_client_t *client) { + client->newstate = NS_CLIENTSTATE_MAX; + if (!ns_g_clienttest && manager != NULL && + !manager->exiting) ++ { + ISC_QUEUE_PUSH(manager->inactive, client, + ilink); +- if (client->needshutdown) ++ } ++ if (client->needshutdown) { + isc_task_shutdown(client->task); ++ } + return (true); + } + } +@@ -675,7 +734,6 @@ client_start(isc_task_t *task, isc_event_t *event) { + } + } + +- + /*% + * The client's task has received a shutdown event. + */ +@@ -2507,17 +2565,12 @@ client_request(isc_task_t *task, isc_event_t *event) { + /* + * Pipeline TCP query processing. + */ +- if (client->message->opcode != dns_opcode_query) ++ if (client->message->opcode != dns_opcode_query) { + client->pipelined = false; ++ } + if (TCP_CLIENT(client) && client->pipelined) { +- result = isc_quota_reserve(&ns_g_server->tcpquota); +- if (result == ISC_R_SUCCESS) +- result = ns_client_replace(client); ++ result = ns_client_replace(client); + if (result != ISC_R_SUCCESS) { +- ns_client_log(client, NS_LOGCATEGORY_CLIENT, +- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING, +- "no more TCP clients(read): %s", +- isc_result_totext(result)); + client->pipelined = false; + } + } +@@ -3087,6 +3140,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { + client->filter_aaaa = dns_aaaa_ok; + #endif + client->needshutdown = ns_g_clienttest; ++ client->tcpactive = false; + + ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL, + NS_EVENT_CLIENTCONTROL, client_start, client, client, +@@ -3100,6 +3154,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { + client->formerrcache.id = 0; + ISC_LINK_INIT(client, link); + ISC_LINK_INIT(client, rlink); ++ ISC_LINK_INIT(client, glink); + ISC_QLINK_INIT(client, ilink); + client->keytag = NULL; + client->keytag_len = 0; +@@ -3193,12 +3248,19 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + + INSIST(client->state == NS_CLIENTSTATE_READY); + ++ /* ++ * The accept() was successful and we're now establishing a new ++ * connection. We need to make note of it in the client and ++ * interface objects so client objects can do the right thing ++ * when going inactive in exit_check() (see comments in ++ * client_accept() for details). ++ */ + INSIST(client->naccepts == 1); + client->naccepts--; + + LOCK(&client->interface->lock); +- INSIST(client->interface->ntcpcurrent > 0); +- client->interface->ntcpcurrent--; ++ INSIST(client->interface->ntcpaccepting > 0); ++ client->interface->ntcpaccepting--; + UNLOCK(&client->interface->lock); + + /* +@@ -3232,6 +3294,9 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), + "accept failed: %s", + isc_result_totext(nevent->result)); ++ if (client->tcpquota != NULL) { ++ isc_quota_detach(&client->tcpquota); ++ } + } + + if (exit_check(client)) +@@ -3270,18 +3335,12 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + * deny service to legitimate TCP clients. + */ + client->pipelined = false; +- result = isc_quota_attach(&ns_g_server->tcpquota, +- &client->tcpquota); +- if (result == ISC_R_SUCCESS) +- result = ns_client_replace(client); +- if (result != ISC_R_SUCCESS) { +- ns_client_log(client, NS_LOGCATEGORY_CLIENT, +- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING, +- "no more TCP clients(accept): %s", +- isc_result_totext(result)); +- } else if (ns_g_server->keepresporder == NULL || +- !allowed(&netaddr, NULL, NULL, 0, NULL, +- ns_g_server->keepresporder)) { ++ result = ns_client_replace(client); ++ if (result == ISC_R_SUCCESS && ++ (client->sctx->keepresporder == NULL || ++ !allowed(&netaddr, NULL, NULL, 0, NULL, ++ ns_g_server->keepresporder))) ++ { + client->pipelined = true; + } + +@@ -3298,12 +3357,80 @@ client_accept(ns_client_t *client) { + + CTRACE("accept"); + ++ /* ++ * The tcpquota object can only be simultaneously referenced a ++ * pre-defined number of times; this is configured by 'tcp-clients' ++ * in named.conf. If we can't attach to it here, that means the TCP ++ * client quota has been exceeded. ++ */ ++ result = isc_quota_attach(&client->sctx->tcpquota, ++ &client->tcpquota); ++ if (result != ISC_R_SUCCESS) { ++ bool exit; ++ ++ ns_client_log(client, NS_LOGCATEGORY_CLIENT, ++ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1), ++ "no more TCP clients: %s", ++ isc_result_totext(result)); ++ ++ /* ++ * We have exceeded the system-wide TCP client ++ * quota. But, we can't just block this accept ++ * in all cases, because if we did, a heavy TCP ++ * load on other interfaces might cause this ++ * interface to be starved, with no clients able ++ * to accept new connections. ++ * ++ * So, we check here to see if any other client ++ * is already servicing TCP queries on this ++ * interface (whether accepting, reading, or ++ * processing). ++ * ++ * If so, then it's okay *not* to call ++ * accept - we can let this client to go inactive ++ * and the other one handle the next connection ++ * when it's ready. ++ * ++ * But if not, then we need to be a little bit ++ * flexible about the quota. We allow *one* extra ++ * TCP client through, to ensure we're listening on ++ * every interface. ++ * ++ * (Note: In practice this means that the *real* ++ * TCP client quota is tcp-clients plus the number ++ * of interfaces.) ++ */ ++ LOCK(&client->interface->lock); ++ exit = (client->interface->ntcpactive > 0); ++ UNLOCK(&client->interface->lock); ++ ++ if (exit) { ++ client->newstate = NS_CLIENTSTATE_INACTIVE; ++ (void)exit_check(client); ++ return; ++ } ++ } ++ ++ /* ++ * By incrementing the interface's ntcpactive counter we signal ++ * that there is at least one client servicing TCP queries for the ++ * interface. ++ * ++ * We also make note of the fact in the client itself with the ++ * tcpactive flag. This ensures proper accounting by preventing ++ * us from accidentally incrementing or decrementing ntcpactive ++ * more than once per client object. ++ */ ++ if (!client->tcpactive) { ++ LOCK(&client->interface->lock); ++ client->interface->ntcpactive++; ++ UNLOCK(&client->interface->lock); ++ client->tcpactive = true; ++ } ++ + result = isc_socket_accept(client->tcplistener, client->task, + client_newconn, client); + if (result != ISC_R_SUCCESS) { +- UNEXPECTED_ERROR(__FILE__, __LINE__, +- "isc_socket_accept() failed: %s", +- isc_result_totext(result)); + /* + * XXXRTH What should we do? We're trying to accept but + * it didn't work. If we just give up, then TCP +@@ -3311,12 +3438,39 @@ client_accept(ns_client_t *client) { + * + * For now, we just go idle. + */ ++ UNEXPECTED_ERROR(__FILE__, __LINE__, ++ "isc_socket_accept() failed: %s", ++ isc_result_totext(result)); ++ if (client->tcpquota != NULL) { ++ isc_quota_detach(&client->tcpquota); ++ } + return; + } ++ ++ /* ++ * The client's 'naccepts' counter indicates that this client has ++ * called accept() and is waiting for a new connection. It should ++ * never exceed 1. ++ */ + INSIST(client->naccepts == 0); + client->naccepts++; ++ ++ /* ++ * The interface's 'ntcpaccepting' counter is incremented when ++ * any client calls accept(), and decremented in client_newconn() ++ * once the connection is established. ++ * ++ * When the client object is shutting down after handling a TCP ++ * request (see exit_check()), it looks to see whether this value is ++ * non-zero. If so, that means another client has already called ++ * accept() and is waiting to establish the next connection, which ++ * means the first client is free to go inactive. Otherwise, ++ * the first client must come back and call accept() again; this ++ * guarantees there will always be at least one client listening ++ * for new TCP connections on each interface. ++ */ + LOCK(&client->interface->lock); +- client->interface->ntcpcurrent++; ++ client->interface->ntcpaccepting++; + UNLOCK(&client->interface->lock); + } + +@@ -3390,13 +3544,14 @@ ns_client_replace(ns_client_t *client) { + tcp = TCP_CLIENT(client); + if (tcp && client->pipelined) { + result = get_worker(client->manager, client->interface, +- client->tcpsocket); ++ client->tcpsocket, client); + } else { + result = get_client(client->manager, client->interface, + client->dispatch, tcp); + } +- if (result != ISC_R_SUCCESS) ++ if (result != ISC_R_SUCCESS) { + return (result); ++ } + + /* + * The responsibility for listening for new requests is hereby +@@ -3585,6 +3740,7 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + client->attributes |= NS_CLIENTATTR_TCP; + isc_socket_attach(ifp->tcpsocket, + &client->tcplistener); ++ + } else { + isc_socket_t *sock; + +@@ -3602,7 +3758,8 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + } + + static isc_result_t +-get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock) ++get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock, ++ ns_client_t *oldclient) + { + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *ev; +@@ -3610,6 +3767,7 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock) + MTRACE("get worker"); + + REQUIRE(manager != NULL); ++ REQUIRE(oldclient != NULL); + + if (manager->exiting) + return (ISC_R_SHUTTINGDOWN); +@@ -3642,7 +3800,28 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock) + ns_interface_attach(ifp, &client->interface); + client->newstate = client->state = NS_CLIENTSTATE_WORKING; + INSIST(client->recursionquota == NULL); +- client->tcpquota = &ns_g_server->tcpquota; ++ ++ /* ++ * Transfer TCP quota to the new client. ++ */ ++ INSIST(client->tcpquota == NULL); ++ INSIST(oldclient->tcpquota != NULL); ++ client->tcpquota = oldclient->tcpquota; ++ oldclient->tcpquota = NULL; ++ ++ /* ++ * Link to a pipeline group, creating it if needed. ++ */ ++ if (!ISC_LINK_LINKED(oldclient, glink)) { ++ oldclient->glink.next = NULL; ++ oldclient->glink.prev = NULL; ++ } ++ client->glink.next = oldclient->glink.next; ++ client->glink.prev = oldclient; ++ if (oldclient->glink.next != NULL) { ++ oldclient->glink.next->glink.prev = client; ++ } ++ oldclient->glink.next = client; + + client->dscp = ifp->dscp; + +@@ -3656,6 +3835,12 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock) + (void)isc_socket_getpeername(client->tcpsocket, &client->peeraddr); + client->peeraddr_valid = true; + ++ LOCK(&client->interface->lock); ++ client->interface->ntcpactive++; ++ UNLOCK(&client->interface->lock); ++ ++ client->tcpactive = true; ++ + INSIST(client->tcpmsg_valid == false); + dns_tcpmsg_init(client->mctx, client->tcpsocket, &client->tcpmsg); + client->tcpmsg_valid = true; +diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h +index b23a7b191d..1f7973f9c5 100644 +--- a/bin/named/include/named/client.h ++++ b/bin/named/include/named/client.h +@@ -94,7 +94,8 @@ struct ns_client { + int nupdates; + int nctls; + int references; +- bool needshutdown; /* ++ bool tcpactive; ++ bool needshutdown; /* + * Used by clienttest to get + * the client to go from + * inactive to free state +@@ -130,9 +131,9 @@ struct ns_client { + isc_stdtime_t now; + isc_time_t tnow; + dns_name_t signername; /*%< [T]SIG key name */ +- dns_name_t * signer; /*%< NULL if not valid sig */ +- bool mortal; /*%< Die after handling request */ +- bool pipelined; /*%< TCP queries not in sequence */ ++ dns_name_t *signer; /*%< NULL if not valid sig */ ++ bool mortal; /*%< Die after handling request */ ++ bool pipelined; /*%< TCP queries not in sequence */ + isc_quota_t *tcpquota; + isc_quota_t *recursionquota; + ns_interface_t *interface; +@@ -143,8 +144,8 @@ struct ns_client { + isc_sockaddr_t destsockaddr; + + isc_netaddr_t ecs_addr; /*%< EDNS client subnet */ +- uint8_t ecs_addrlen; +- uint8_t ecs_scope; ++ uint8_t ecs_addrlen; ++ uint8_t ecs_scope; + + struct in6_pktinfo pktinfo; + isc_dscp_t dscp; +@@ -166,6 +167,7 @@ struct ns_client { + + ISC_LINK(ns_client_t) link; + ISC_LINK(ns_client_t) rlink; ++ ISC_LINK(ns_client_t) glink; + ISC_QLINK(ns_client_t) ilink; + unsigned char cookie[8]; + uint32_t expire; +diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h +index 7d1883e1e8..61b08826a6 100644 +--- a/bin/named/include/named/interfacemgr.h ++++ b/bin/named/include/named/interfacemgr.h +@@ -77,9 +77,14 @@ struct ns_interface { + /*%< UDP dispatchers. */ + isc_socket_t * tcpsocket; /*%< TCP socket. */ + isc_dscp_t dscp; /*%< "listen-on" DSCP value */ +- int ntcptarget; /*%< Desired number of concurrent +- TCP accepts */ +- int ntcpcurrent; /*%< Current ditto, locked */ ++ int ntcpaccepting; /*%< Number of clients ++ ready to accept new ++ TCP connections on this ++ interface */ ++ int ntcpactive; /*%< Number of clients ++ servicing TCP queries ++ (whether accepting or ++ connected) */ + int nudpdispatch; /*%< Number of UDP dispatches */ + ns_clientmgr_t * clientmgr; /*%< Client manager. */ + ISC_LINK(ns_interface_t) link; +diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c +index 419927bf54..955096ef47 100644 +--- a/bin/named/interfacemgr.c ++++ b/bin/named/interfacemgr.c +@@ -386,8 +386,8 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, + * connections will be handled in parallel even though there is + * only one client initially. + */ +- ifp->ntcptarget = 1; +- ifp->ntcpcurrent = 0; ++ ifp->ntcpaccepting = 0; ++ ifp->ntcpactive = 0; + ifp->nudpdispatch = 0; + + ifp->dscp = -1; +@@ -522,9 +522,7 @@ ns_interface_accepttcp(ns_interface_t *ifp) { + */ + (void)isc_socket_filter(ifp->tcpsocket, "dataready"); + +- result = ns_clientmgr_createclients(ifp->clientmgr, +- ifp->ntcptarget, ifp, +- true); ++ result = ns_clientmgr_createclients(ifp->clientmgr, 1, ifp, true); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "TCP ns_clientmgr_createclients(): %s", +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0003-use-reference-counter-for-pipeline-groups-v3.patch b/meta/recipes-connectivity/bind/bind/0003-use-reference-counter-for-pipeline-groups-v3.patch new file mode 100644 index 0000000000..032cfb8c44 --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0003-use-reference-counter-for-pipeline-groups-v3.patch @@ -0,0 +1,278 @@ +Backport patch to fix CVE-2018-5743. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2018-5743 + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/366b4e1] + +Signed-off-by: Kai Kang + +From 366b4e1ede8aed690e981e07137cb1cb77879c36 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= +Date: Thu, 17 Jan 2019 15:53:38 +0100 +Subject: [PATCH 3/6] use reference counter for pipeline groups (v3) + +Track pipeline groups using a shared reference counter +instead of a linked list. + +(cherry picked from commit 513afd33eb17d5dc41a3f0d2d38204ef8c5f6f91) +(cherry picked from commit 9446629b730c59c4215f08d37fbaf810282fbccb) +--- + bin/named/client.c | 171 ++++++++++++++++++++----------- + bin/named/include/named/client.h | 2 +- + 2 files changed, 110 insertions(+), 63 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index a7b49a0f71..277656cef0 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -299,6 +299,75 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) { + } + } + ++/*% ++ * Allocate a reference counter that will track the number of client structures ++ * using the TCP connection that 'client' called accept() for. This counter ++ * will be shared between all client structures associated with this TCP ++ * connection. ++ */ ++static void ++pipeline_init(ns_client_t *client) { ++ isc_refcount_t *refs; ++ ++ REQUIRE(client->pipeline_refs == NULL); ++ ++ /* ++ * A global memory context is used for the allocation as different ++ * client structures may have different memory contexts assigned and a ++ * reference counter allocated here might need to be freed by a ++ * different client. The performance impact caused by memory context ++ * contention here is expected to be negligible, given that this code ++ * is only executed for TCP connections. ++ */ ++ refs = isc_mem_allocate(client->sctx->mctx, sizeof(*refs)); ++ isc_refcount_init(refs, 1); ++ client->pipeline_refs = refs; ++} ++ ++/*% ++ * Increase the count of client structures using the TCP connection that ++ * 'source' is associated with and put a pointer to that count in 'target', ++ * thus associating it with the same TCP connection. ++ */ ++static void ++pipeline_attach(ns_client_t *source, ns_client_t *target) { ++ int old_refs; ++ ++ REQUIRE(source->pipeline_refs != NULL); ++ REQUIRE(target->pipeline_refs == NULL); ++ ++ old_refs = isc_refcount_increment(source->pipeline_refs); ++ INSIST(old_refs > 0); ++ target->pipeline_refs = source->pipeline_refs; ++} ++ ++/*% ++ * Decrease the count of client structures using the TCP connection that ++ * 'client' is associated with. If this is the last client using this TCP ++ * connection, free the reference counter and return true; otherwise, return ++ * false. ++ */ ++static bool ++pipeline_detach(ns_client_t *client) { ++ isc_refcount_t *refs; ++ int old_refs; ++ ++ REQUIRE(client->pipeline_refs != NULL); ++ ++ refs = client->pipeline_refs; ++ client->pipeline_refs = NULL; ++ ++ old_refs = isc_refcount_decrement(refs); ++ INSIST(old_refs > 0); ++ ++ if (old_refs == 1) { ++ isc_mem_free(client->sctx->mctx, refs); ++ return (true); ++ } ++ ++ return (false); ++} ++ + /*% + * Check for a deactivation or shutdown request and take appropriate + * action. Returns true if either is in progress; in this case +@@ -421,6 +490,40 @@ exit_check(ns_client_t *client) { + client->tcpmsg_valid = false; + } + ++ if (client->tcpquota != NULL) { ++ if (client->pipeline_refs == NULL || ++ pipeline_detach(client)) ++ { ++ /* ++ * Only detach from the TCP client quota if ++ * there are no more client structures using ++ * this TCP connection. ++ * ++ * Note that we check 'pipeline_refs' and not ++ * 'pipelined' because in some cases (e.g. ++ * after receiving a request with an opcode ++ * different than QUERY) 'pipelined' is set to ++ * false after the reference counter gets ++ * allocated in pipeline_init() and we must ++ * still drop our reference as failing to do so ++ * would prevent the reference counter itself ++ * from being freed. ++ */ ++ isc_quota_detach(&client->tcpquota); ++ } else { ++ /* ++ * There are other client structures using this ++ * TCP connection, so we cannot detach from the ++ * TCP client quota to prevent excess TCP ++ * connections from being accepted. However, ++ * this client structure might later be reused ++ * for accepting new connections and thus must ++ * have its 'tcpquota' field set to NULL. ++ */ ++ client->tcpquota = NULL; ++ } ++ } ++ + if (client->tcpsocket != NULL) { + CTRACE("closetcp"); + isc_socket_detach(&client->tcpsocket); +@@ -434,44 +537,6 @@ exit_check(ns_client_t *client) { + } + } + +- if (client->tcpquota != NULL) { +- /* +- * If we are not in a pipeline group, or +- * we are the last client in the group, detach from +- * tcpquota; otherwise, transfer the quota to +- * another client in the same group. +- */ +- if (!ISC_LINK_LINKED(client, glink) || +- (client->glink.next == NULL && +- client->glink.prev == NULL)) +- { +- isc_quota_detach(&client->tcpquota); +- } else if (client->glink.next != NULL) { +- INSIST(client->glink.next->tcpquota == NULL); +- client->glink.next->tcpquota = client->tcpquota; +- client->tcpquota = NULL; +- } else { +- INSIST(client->glink.prev->tcpquota == NULL); +- client->glink.prev->tcpquota = client->tcpquota; +- client->tcpquota = NULL; +- } +- } +- +- /* +- * Unlink from pipeline group. +- */ +- if (ISC_LINK_LINKED(client, glink)) { +- if (client->glink.next != NULL) { +- client->glink.next->glink.prev = +- client->glink.prev; +- } +- if (client->glink.prev != NULL) { +- client->glink.prev->glink.next = +- client->glink.next; +- } +- ISC_LINK_INIT(client, glink); +- } +- + if (client->timerset) { + (void)isc_timer_reset(client->timer, + isc_timertype_inactive, +@@ -3130,6 +3195,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { + dns_name_init(&client->signername, NULL); + client->mortal = false; + client->pipelined = false; ++ client->pipeline_refs = NULL; + client->tcpquota = NULL; + client->recursionquota = NULL; + client->interface = NULL; +@@ -3154,7 +3220,6 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { + client->formerrcache.id = 0; + ISC_LINK_INIT(client, link); + ISC_LINK_INIT(client, rlink); +- ISC_LINK_INIT(client, glink); + ISC_QLINK_INIT(client, ilink); + client->keytag = NULL; + client->keytag_len = 0; +@@ -3341,6 +3406,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + !allowed(&netaddr, NULL, NULL, 0, NULL, + ns_g_server->keepresporder))) + { ++ pipeline_init(client); + client->pipelined = true; + } + +@@ -3800,35 +3866,16 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock, + ns_interface_attach(ifp, &client->interface); + client->newstate = client->state = NS_CLIENTSTATE_WORKING; + INSIST(client->recursionquota == NULL); +- +- /* +- * Transfer TCP quota to the new client. +- */ +- INSIST(client->tcpquota == NULL); +- INSIST(oldclient->tcpquota != NULL); +- client->tcpquota = oldclient->tcpquota; +- oldclient->tcpquota = NULL; +- +- /* +- * Link to a pipeline group, creating it if needed. +- */ +- if (!ISC_LINK_LINKED(oldclient, glink)) { +- oldclient->glink.next = NULL; +- oldclient->glink.prev = NULL; +- } +- client->glink.next = oldclient->glink.next; +- client->glink.prev = oldclient; +- if (oldclient->glink.next != NULL) { +- oldclient->glink.next->glink.prev = client; +- } +- oldclient->glink.next = client; ++ client->tcpquota = &client->sctx->tcpquota; + + client->dscp = ifp->dscp; + + client->attributes |= NS_CLIENTATTR_TCP; +- client->pipelined = true; + client->mortal = true; + ++ pipeline_attach(oldclient, client); ++ client->pipelined = true; ++ + isc_socket_attach(ifp->tcpsocket, &client->tcplistener); + isc_socket_attach(sock, &client->tcpsocket); + isc_socket_setname(client->tcpsocket, "worker-tcp", NULL); +diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h +index 1f7973f9c5..aeed9ccdda 100644 +--- a/bin/named/include/named/client.h ++++ b/bin/named/include/named/client.h +@@ -134,6 +134,7 @@ struct ns_client { + dns_name_t *signer; /*%< NULL if not valid sig */ + bool mortal; /*%< Die after handling request */ + bool pipelined; /*%< TCP queries not in sequence */ ++ isc_refcount_t *pipeline_refs; + isc_quota_t *tcpquota; + isc_quota_t *recursionquota; + ns_interface_t *interface; +@@ -167,7 +168,6 @@ struct ns_client { + + ISC_LINK(ns_client_t) link; + ISC_LINK(ns_client_t) rlink; +- ISC_LINK(ns_client_t) glink; + ISC_QLINK(ns_client_t) ilink; + unsigned char cookie[8]; + uint32_t expire; +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch b/meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch new file mode 100644 index 0000000000..034ab13303 --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch @@ -0,0 +1,512 @@ +Backport patch to fix CVE-2018-5743. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2018-5743 + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/2ab8a08] + +Signed-off-by: Kai Kang + +From 2ab8a085b3c666f28f1f9229bd6ecb59915b26c3 Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Fri, 5 Apr 2019 16:12:18 -0700 +Subject: [PATCH 4/6] better tcpquota accounting and client mortality checks + +- ensure that tcpactive is cleaned up correctly when accept() fails. +- set 'client->tcpattached' when the client is attached to the tcpquota. + carry this value on to new clients sharing the same pipeline group. + don't call isc_quota_detach() on the tcpquota unless tcpattached is + set. this way clients that were allowed to accept TCP connections + despite being over quota (and therefore, were never attached to the + quota) will not inadvertently detach from it and mess up the + accounting. +- simplify the code for tcpquota disconnection by using a new function + tcpquota_disconnect(). +- before deciding whether to reject a new connection due to quota + exhaustion, check to see whether there are at least two active + clients. previously, this was "at least one", but that could be + insufficient if there was one other client in READING state (waiting + for messages on an open connection) but none in READY (listening + for new connections). +- before deciding whether a TCP client object can to go inactive, we + must ensure there are enough other clients to maintain service + afterward -- both accepting new connections and reading/processing new + queries. A TCP client can't shut down unless at least one + client is accepting new connections and (in the case of pipelined + clients) at least one additional client is waiting to read. + +(cherry picked from commit c7394738b2445c16f728a88394864dd61baad900) +(cherry picked from commit e965d5f11d3d0f6d59704e614fceca2093cb1856) +(cherry picked from commit 87d431161450777ea093821212abfb52d51b36e3) +--- + bin/named/client.c | 244 +++++++++++++++++++------------ + bin/named/include/named/client.h | 3 +- + 2 files changed, 152 insertions(+), 95 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index 277656cef0..61e96dd28c 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -244,13 +244,14 @@ static void client_start(isc_task_t *task, isc_event_t *event); + static void client_request(isc_task_t *task, isc_event_t *event); + static void ns_client_dumpmessage(ns_client_t *client, const char *reason); + static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, +- dns_dispatch_t *disp, bool tcp); ++ dns_dispatch_t *disp, ns_client_t *oldclient, ++ bool tcp); + static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, + isc_socket_t *sock, ns_client_t *oldclient); + static inline bool + allowed(isc_netaddr_t *addr, dns_name_t *signer, + isc_netaddr_t *ecs_addr, uint8_t ecs_addrlen, +- uint8_t *ecs_scope, dns_acl_t *acl) ++ uint8_t *ecs_scope, dns_acl_t *acl); + static void compute_cookie(ns_client_t *client, uint32_t when, + uint32_t nonce, const unsigned char *secret, + isc_buffer_t *buf); +@@ -319,7 +320,7 @@ pipeline_init(ns_client_t *client) { + * contention here is expected to be negligible, given that this code + * is only executed for TCP connections. + */ +- refs = isc_mem_allocate(client->sctx->mctx, sizeof(*refs)); ++ refs = isc_mem_allocate(ns_g_mctx, sizeof(*refs)); + isc_refcount_init(refs, 1); + client->pipeline_refs = refs; + } +@@ -331,13 +332,13 @@ pipeline_init(ns_client_t *client) { + */ + static void + pipeline_attach(ns_client_t *source, ns_client_t *target) { +- int old_refs; ++ int refs; + + REQUIRE(source->pipeline_refs != NULL); + REQUIRE(target->pipeline_refs == NULL); + +- old_refs = isc_refcount_increment(source->pipeline_refs); +- INSIST(old_refs > 0); ++ isc_refcount_increment(source->pipeline_refs, &refs); ++ INSIST(refs > 1); + target->pipeline_refs = source->pipeline_refs; + } + +@@ -349,25 +350,51 @@ pipeline_attach(ns_client_t *source, ns_client_t *target) { + */ + static bool + pipeline_detach(ns_client_t *client) { +- isc_refcount_t *refs; +- int old_refs; ++ isc_refcount_t *refcount; ++ int refs; + + REQUIRE(client->pipeline_refs != NULL); + +- refs = client->pipeline_refs; ++ refcount = client->pipeline_refs; + client->pipeline_refs = NULL; + +- old_refs = isc_refcount_decrement(refs); +- INSIST(old_refs > 0); ++ isc_refcount_decrement(refcount, refs); + +- if (old_refs == 1) { +- isc_mem_free(client->sctx->mctx, refs); ++ if (refs == 0) { ++ isc_mem_free(ns_g_mctx, refs); + return (true); + } + + return (false); + } + ++/* ++ * Detach a client from the TCP client quota if appropriate, and set ++ * the quota pointer to NULL. ++ * ++ * Sometimes when the TCP client quota is exhausted but there are no other ++ * clients servicing the interface, a client will be allowed to continue ++ * running despite not having been attached to the quota. In this event, ++ * the TCP quota was never attached to the client, so when the client (or ++ * associated pipeline group) shuts down, the quota must NOT be detached. ++ * ++ * Otherwise, if the quota pointer is set, it should be detached. If not ++ * set at all, we just return without doing anything. ++ */ ++static void ++tcpquota_disconnect(ns_client_t *client) { ++ if (client->tcpquota == NULL) { ++ return; ++ } ++ ++ if (client->tcpattached) { ++ isc_quota_detach(&client->tcpquota); ++ client->tcpattached = false; ++ } else { ++ client->tcpquota = NULL; ++ } ++} ++ + /*% + * Check for a deactivation or shutdown request and take appropriate + * action. Returns true if either is in progress; in this case +@@ -490,38 +517,31 @@ exit_check(ns_client_t *client) { + client->tcpmsg_valid = false; + } + +- if (client->tcpquota != NULL) { +- if (client->pipeline_refs == NULL || +- pipeline_detach(client)) +- { +- /* +- * Only detach from the TCP client quota if +- * there are no more client structures using +- * this TCP connection. +- * +- * Note that we check 'pipeline_refs' and not +- * 'pipelined' because in some cases (e.g. +- * after receiving a request with an opcode +- * different than QUERY) 'pipelined' is set to +- * false after the reference counter gets +- * allocated in pipeline_init() and we must +- * still drop our reference as failing to do so +- * would prevent the reference counter itself +- * from being freed. +- */ +- isc_quota_detach(&client->tcpquota); +- } else { +- /* +- * There are other client structures using this +- * TCP connection, so we cannot detach from the +- * TCP client quota to prevent excess TCP +- * connections from being accepted. However, +- * this client structure might later be reused +- * for accepting new connections and thus must +- * have its 'tcpquota' field set to NULL. +- */ +- client->tcpquota = NULL; +- } ++ /* ++ * Detach from pipeline group and from TCP client quota, ++ * if appropriate. ++ * ++ * - If no pipeline group is active, attempt to ++ * detach from the TCP client quota. ++ * ++ * - If a pipeline group is active, detach from it; ++ * if the return code indicates that there no more ++ * clients left if this pipeline group, we also detach ++ * from the TCP client quota. ++ * ++ * - Otherwise we don't try to detach, we just set the ++ * TCP quota pointer to NULL if it wasn't NULL already. ++ * ++ * tcpquota_disconnect() will set tcpquota to NULL, either ++ * by detaching it or by assignment, depending on the ++ * needs of the client. See the comments on that function ++ * for further information. ++ */ ++ if (client->pipeline_refs == NULL || pipeline_detach(client)) { ++ tcpquota_disconnect(client); ++ } else { ++ client->tcpquota = NULL; ++ client->tcpattached = false; + } + + if (client->tcpsocket != NULL) { +@@ -544,8 +564,6 @@ exit_check(ns_client_t *client) { + client->timerset = false; + } + +- client->pipelined = false; +- + client->peeraddr_valid = false; + + client->state = NS_CLIENTSTATE_READY; +@@ -558,18 +576,27 @@ exit_check(ns_client_t *client) { + * active and force it to go inactive if not. + * + * UDP clients go inactive at this point, but a TCP client +- * will needs to remain active if no other clients are +- * listening for TCP requests on this interface, to +- * prevent this interface from going nonresponsive. ++ * may need to remain active and go into ready state if ++ * no other clients are available to listen for TCP ++ * requests on this interface or (in the case of pipelined ++ * clients) to read for additional messages on the current ++ * connection. + */ + if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) { + LOCK(&client->interface->lock); +- if (client->interface->ntcpaccepting == 0) { ++ if ((client->interface->ntcpaccepting == 0 || ++ (client->pipelined && ++ client->interface->ntcpactive < 2)) && ++ client->newstate != NS_CLIENTSTATE_FREED) ++ { + client->mortal = false; ++ client->newstate = NS_CLIENTSTATE_READY; + } + UNLOCK(&client->interface->lock); + } + ++ client->pipelined = false; ++ + /* + * We don't need the client; send it to the inactive + * queue for recycling. +@@ -2634,6 +2661,18 @@ client_request(isc_task_t *task, isc_event_t *event) { + client->pipelined = false; + } + if (TCP_CLIENT(client) && client->pipelined) { ++ /* ++ * We're pipelining. Replace the client; the ++ * the replacement can read the TCP socket looking ++ * for new messages and this client can process the ++ * current message asynchronously. ++ * ++ * There are now at least three clients using this ++ * TCP socket - one accepting new connections, ++ * one reading an existing connection to get new ++ * messages, and one answering the message already ++ * received. ++ */ + result = ns_client_replace(client); + if (result != ISC_R_SUCCESS) { + client->pipelined = false; +@@ -3197,6 +3236,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { + client->pipelined = false; + client->pipeline_refs = NULL; + client->tcpquota = NULL; ++ client->tcpattached = false; + client->recursionquota = NULL; + client->interface = NULL; + client->peeraddr_valid = false; +@@ -3359,9 +3399,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), + "accept failed: %s", + isc_result_totext(nevent->result)); +- if (client->tcpquota != NULL) { +- isc_quota_detach(&client->tcpquota); +- } ++ tcpquota_disconnect(client); + } + + if (exit_check(client)) +@@ -3402,7 +3440,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + client->pipelined = false; + result = ns_client_replace(client); + if (result == ISC_R_SUCCESS && +- (client->sctx->keepresporder == NULL || ++ (ns_g_server->keepresporder == NULL || + !allowed(&netaddr, NULL, NULL, 0, NULL, + ns_g_server->keepresporder))) + { +@@ -3429,7 +3467,7 @@ client_accept(ns_client_t *client) { + * in named.conf. If we can't attach to it here, that means the TCP + * client quota has been exceeded. + */ +- result = isc_quota_attach(&client->sctx->tcpquota, ++ result = isc_quota_attach(&ns_g_server->tcpquota, + &client->tcpquota); + if (result != ISC_R_SUCCESS) { + bool exit; +@@ -3447,27 +3485,27 @@ client_accept(ns_client_t *client) { + * interface to be starved, with no clients able + * to accept new connections. + * +- * So, we check here to see if any other client +- * is already servicing TCP queries on this ++ * So, we check here to see if any other clients ++ * are already servicing TCP queries on this + * interface (whether accepting, reading, or +- * processing). +- * +- * If so, then it's okay *not* to call +- * accept - we can let this client to go inactive +- * and the other one handle the next connection +- * when it's ready. ++ * processing). If there are at least two ++ * (one reading and one processing a request) ++ * then it's okay *not* to call accept - we ++ * can let this client go inactive and another ++ * one will resume accepting when it's done. + * +- * But if not, then we need to be a little bit +- * flexible about the quota. We allow *one* extra +- * TCP client through, to ensure we're listening on +- * every interface. ++ * If there aren't enough active clients on the ++ * interface, then we can be a little bit ++ * flexible about the quota. We'll allow *one* ++ * extra client through to ensure we're listening ++ * on every interface. + * +- * (Note: In practice this means that the *real* +- * TCP client quota is tcp-clients plus the number +- * of interfaces.) ++ * (Note: In practice this means that the real ++ * TCP client quota is tcp-clients plus the ++ * number of listening interfaces plus 2.) + */ + LOCK(&client->interface->lock); +- exit = (client->interface->ntcpactive > 0); ++ exit = (client->interface->ntcpactive > 1); + UNLOCK(&client->interface->lock); + + if (exit) { +@@ -3475,6 +3513,9 @@ client_accept(ns_client_t *client) { + (void)exit_check(client); + return; + } ++ ++ } else { ++ client->tcpattached = true; + } + + /* +@@ -3507,9 +3548,16 @@ client_accept(ns_client_t *client) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_socket_accept() failed: %s", + isc_result_totext(result)); +- if (client->tcpquota != NULL) { +- isc_quota_detach(&client->tcpquota); ++ ++ tcpquota_disconnect(client); ++ ++ if (client->tcpactive) { ++ LOCK(&client->interface->lock); ++ client->interface->ntcpactive--; ++ UNLOCK(&client->interface->lock); ++ client->tcpactive = false; + } ++ + return; + } + +@@ -3527,13 +3575,12 @@ client_accept(ns_client_t *client) { + * once the connection is established. + * + * When the client object is shutting down after handling a TCP +- * request (see exit_check()), it looks to see whether this value is +- * non-zero. If so, that means another client has already called +- * accept() and is waiting to establish the next connection, which +- * means the first client is free to go inactive. Otherwise, +- * the first client must come back and call accept() again; this +- * guarantees there will always be at least one client listening +- * for new TCP connections on each interface. ++ * request (see exit_check()), if this value is at least one, that ++ * means another client has called accept() and is waiting to ++ * establish the next connection. That means the client may be ++ * be free to become inactive; otherwise it may need to start ++ * listening for connections itself to prevent the interface ++ * going dead. + */ + LOCK(&client->interface->lock); + client->interface->ntcpaccepting++; +@@ -3613,19 +3660,19 @@ ns_client_replace(ns_client_t *client) { + client->tcpsocket, client); + } else { + result = get_client(client->manager, client->interface, +- client->dispatch, tcp); ++ client->dispatch, client, tcp); ++ ++ /* ++ * The responsibility for listening for new requests is hereby ++ * transferred to the new client. Therefore, the old client ++ * should refrain from listening for any more requests. ++ */ ++ client->mortal = true; + } + if (result != ISC_R_SUCCESS) { + return (result); + } + +- /* +- * The responsibility for listening for new requests is hereby +- * transferred to the new client. Therefore, the old client +- * should refrain from listening for any more requests. +- */ +- client->mortal = true; +- + return (ISC_R_SUCCESS); + } + +@@ -3759,7 +3806,7 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) { + + static isc_result_t + get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, +- dns_dispatch_t *disp, bool tcp) ++ dns_dispatch_t *disp, ns_client_t *oldclient, bool tcp) + { + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *ev; +@@ -3803,6 +3850,16 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + client->dscp = ifp->dscp; + + if (tcp) { ++ client->tcpattached = false; ++ if (oldclient != NULL) { ++ client->tcpattached = oldclient->tcpattached; ++ } ++ ++ LOCK(&client->interface->lock); ++ client->interface->ntcpactive++; ++ UNLOCK(&client->interface->lock); ++ client->tcpactive = true; ++ + client->attributes |= NS_CLIENTATTR_TCP; + isc_socket_attach(ifp->tcpsocket, + &client->tcplistener); +@@ -3866,7 +3923,8 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock, + ns_interface_attach(ifp, &client->interface); + client->newstate = client->state = NS_CLIENTSTATE_WORKING; + INSIST(client->recursionquota == NULL); +- client->tcpquota = &client->sctx->tcpquota; ++ client->tcpquota = &ns_g_server->tcpquota; ++ client->tcpattached = oldclient->tcpattached; + + client->dscp = ifp->dscp; + +@@ -3885,7 +3943,6 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock, + LOCK(&client->interface->lock); + client->interface->ntcpactive++; + UNLOCK(&client->interface->lock); +- + client->tcpactive = true; + + INSIST(client->tcpmsg_valid == false); +@@ -3913,7 +3970,8 @@ ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n, + MTRACE("createclients"); + + for (disp = 0; disp < n; disp++) { +- result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp); ++ result = get_client(manager, ifp, ifp->udpdispatch[disp], ++ NULL, tcp); + if (result != ISC_R_SUCCESS) + break; + } +diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h +index aeed9ccdda..e2c40acd28 100644 +--- a/bin/named/include/named/client.h ++++ b/bin/named/include/named/client.h +@@ -9,8 +9,6 @@ + * information regarding copyright ownership. + */ + +-/* $Id: client.h,v 1.96 2012/01/31 23:47:31 tbox Exp $ */ +- + #ifndef NAMED_CLIENT_H + #define NAMED_CLIENT_H 1 + +@@ -136,6 +134,7 @@ struct ns_client { + bool pipelined; /*%< TCP queries not in sequence */ + isc_refcount_t *pipeline_refs; + isc_quota_t *tcpquota; ++ bool tcpattached; + isc_quota_t *recursionquota; + ns_interface_t *interface; + +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch b/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch new file mode 100644 index 0000000000..987e75bc0e --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch @@ -0,0 +1,911 @@ +Backport patch to fix CVE-2018-5743. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2018-5743 + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/c47ccf6] + +Signed-off-by: Kai Kang + +From c47ccf630f147378568b33e8fdb7b754f228c346 Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Fri, 5 Apr 2019 16:26:05 -0700 +Subject: [PATCH 5/6] refactor tcpquota and pipeline refs; allow special-case + overrun in isc_quota + +- if the TCP quota has been exceeded but there are no clients listening + for new connections on the interface, we can now force attachment to the + quota using isc_quota_force(), instead of carrying on with the quota not + attached. +- the TCP client quota is now referenced via a reference-counted + 'ns_tcpconn' object, one of which is created whenever a client begins + listening for new connections, and attached to by members of that + client's pipeline group. when the last reference to the tcpconn + object is detached, it is freed and the TCP quota slot is released. +- reduce code duplication by adding mark_tcp_active() function. +- convert counters to atomic. + +(cherry picked from commit 7e8222378ca24f1302a0c1c638565050ab04681b) +(cherry picked from commit 4939451275722bfda490ea86ca13e84f6bc71e46) +(cherry picked from commit 13f7c918b8720d890408f678bd73c20e634539d9) +--- + bin/named/client.c | 444 +++++++++++-------------- + bin/named/include/named/client.h | 12 +- + bin/named/include/named/interfacemgr.h | 6 +- + bin/named/interfacemgr.c | 1 + + lib/isc/include/isc/quota.h | 7 + + lib/isc/quota.c | 33 +- + lib/isc/win32/libisc.def.in | 1 + + 7 files changed, 236 insertions(+), 268 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index 61e96dd28c..d826ab32bf 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -244,8 +244,7 @@ static void client_start(isc_task_t *task, isc_event_t *event); + static void client_request(isc_task_t *task, isc_event_t *event); + static void ns_client_dumpmessage(ns_client_t *client, const char *reason); + static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, +- dns_dispatch_t *disp, ns_client_t *oldclient, +- bool tcp); ++ dns_dispatch_t *disp, bool tcp); + static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, + isc_socket_t *sock, ns_client_t *oldclient); + static inline bool +@@ -301,16 +300,32 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) { + } + + /*% +- * Allocate a reference counter that will track the number of client structures +- * using the TCP connection that 'client' called accept() for. This counter +- * will be shared between all client structures associated with this TCP +- * connection. ++ * Allocate a reference-counted object that will maintain a single pointer to ++ * the (also reference-counted) TCP client quota, shared between all the ++ * clients processing queries on a single TCP connection, so that all ++ * clients sharing the one socket will together consume only one slot in ++ * the 'tcp-clients' quota. + */ +-static void +-pipeline_init(ns_client_t *client) { +- isc_refcount_t *refs; ++static isc_result_t ++tcpconn_init(ns_client_t *client, bool force) { ++ isc_result_t result; ++ isc_quota_t *quota = NULL; ++ ns_tcpconn_t *tconn = NULL; + +- REQUIRE(client->pipeline_refs == NULL); ++ REQUIRE(client->tcpconn == NULL); ++ ++ /* ++ * Try to attach to the quota first, so we won't pointlessly ++ * allocate memory for a tcpconn object if we can't get one. ++ */ ++ if (force) { ++ result = isc_quota_force(&ns_g_server->tcpquota, "a); ++ } else { ++ result = isc_quota_attach(&ns_g_server->tcpquota, "a); ++ } ++ if (result != ISC_R_SUCCESS) { ++ return (result); ++ } + + /* + * A global memory context is used for the allocation as different +@@ -320,78 +335,80 @@ pipeline_init(ns_client_t *client) { + * contention here is expected to be negligible, given that this code + * is only executed for TCP connections. + */ +- refs = isc_mem_allocate(ns_g_mctx, sizeof(*refs)); +- isc_refcount_init(refs, 1); +- client->pipeline_refs = refs; ++ tconn = isc_mem_allocate(ns_g_mctx, sizeof(*tconn)); ++ ++ isc_refcount_init(&tconn->refs, 1); ++ tconn->tcpquota = quota; ++ quota = NULL; ++ tconn->pipelined = false; ++ ++ client->tcpconn = tconn; ++ ++ return (ISC_R_SUCCESS); + } + + /*% +- * Increase the count of client structures using the TCP connection that +- * 'source' is associated with and put a pointer to that count in 'target', +- * thus associating it with the same TCP connection. ++ * Increase the count of client structures sharing the TCP connection ++ * that 'source' is associated with; add a pointer to the same tcpconn ++ * to 'target', thus associating it with the same TCP connection. + */ + static void +-pipeline_attach(ns_client_t *source, ns_client_t *target) { ++tcpconn_attach(ns_client_t *source, ns_client_t *target) { + int refs; + +- REQUIRE(source->pipeline_refs != NULL); +- REQUIRE(target->pipeline_refs == NULL); ++ REQUIRE(source->tcpconn != NULL); ++ REQUIRE(target->tcpconn == NULL); ++ REQUIRE(source->tcpconn->pipelined); + +- isc_refcount_increment(source->pipeline_refs, &refs); ++ isc_refcount_increment(&source->tcpconn->refs, &refs); + INSIST(refs > 1); +- target->pipeline_refs = source->pipeline_refs; ++ target->tcpconn = source->tcpconn; + } + + /*% +- * Decrease the count of client structures using the TCP connection that ++ * Decrease the count of client structures sharing the TCP connection that + * 'client' is associated with. If this is the last client using this TCP +- * connection, free the reference counter and return true; otherwise, return +- * false. ++ * connection, we detach from the TCP quota and free the tcpconn ++ * object. Either way, client->tcpconn is set to NULL. + */ +-static bool +-pipeline_detach(ns_client_t *client) { +- isc_refcount_t *refcount; ++static void ++tcpconn_detach(ns_client_t *client) { ++ ns_tcpconn_t *tconn = NULL; + int refs; + +- REQUIRE(client->pipeline_refs != NULL); +- +- refcount = client->pipeline_refs; +- client->pipeline_refs = NULL; ++ REQUIRE(client->tcpconn != NULL); + +- isc_refcount_decrement(refcount, refs); ++ tconn = client->tcpconn; ++ client->tcpconn = NULL; + ++ isc_refcount_decrement(&tconn->refs, &refs); + if (refs == 0) { +- isc_mem_free(ns_g_mctx, refs); +- return (true); ++ isc_quota_detach(&tconn->tcpquota); ++ isc_mem_free(ns_g_mctx, tconn); + } +- +- return (false); + } + +-/* +- * Detach a client from the TCP client quota if appropriate, and set +- * the quota pointer to NULL. +- * +- * Sometimes when the TCP client quota is exhausted but there are no other +- * clients servicing the interface, a client will be allowed to continue +- * running despite not having been attached to the quota. In this event, +- * the TCP quota was never attached to the client, so when the client (or +- * associated pipeline group) shuts down, the quota must NOT be detached. ++/*% ++ * Mark a client as active and increment the interface's 'ntcpactive' ++ * counter, as a signal that there is at least one client servicing ++ * TCP queries for the interface. If we reach the TCP client quota at ++ * some point, this will be used to determine whether a quota overrun ++ * should be permitted. + * +- * Otherwise, if the quota pointer is set, it should be detached. If not +- * set at all, we just return without doing anything. ++ * Marking the client active with the 'tcpactive' flag ensures proper ++ * accounting, by preventing us from incrementing or decrementing ++ * 'ntcpactive' more than once per client. + */ + static void +-tcpquota_disconnect(ns_client_t *client) { +- if (client->tcpquota == NULL) { +- return; +- } +- +- if (client->tcpattached) { +- isc_quota_detach(&client->tcpquota); +- client->tcpattached = false; +- } else { +- client->tcpquota = NULL; ++mark_tcp_active(ns_client_t *client, bool active) { ++ if (active && !client->tcpactive) { ++ isc_atomic_xadd(&client->interface->ntcpactive, 1); ++ client->tcpactive = active; ++ } else if (!active && client->tcpactive) { ++ uint32_t old = ++ isc_atomic_xadd(&client->interface->ntcpactive, -1); ++ INSIST(old > 0); ++ client->tcpactive = active; + } + } + +@@ -484,7 +501,8 @@ exit_check(ns_client_t *client) { + INSIST(client->recursionquota == NULL); + + if (NS_CLIENTSTATE_READING == client->newstate) { +- if (!client->pipelined) { ++ INSIST(client->tcpconn != NULL); ++ if (!client->tcpconn->pipelined) { + client_read(client); + client->newstate = NS_CLIENTSTATE_MAX; + return (true); /* We're done. */ +@@ -507,8 +525,8 @@ exit_check(ns_client_t *client) { + dns_tcpmsg_cancelread(&client->tcpmsg); + } + +- if (client->nreads != 0) { +- /* Still waiting for read cancel completion. */ ++ /* Still waiting for read cancel completion. */ ++ if (client->nreads > 0) { + return (true); + } + +@@ -518,43 +536,45 @@ exit_check(ns_client_t *client) { + } + + /* +- * Detach from pipeline group and from TCP client quota, +- * if appropriate. ++ * Soon the client will be ready to accept a new TCP ++ * connection or UDP request, but we may have enough ++ * clients doing that already. Check whether this client ++ * needs to remain active and allow it go inactive if ++ * not. + * +- * - If no pipeline group is active, attempt to +- * detach from the TCP client quota. ++ * UDP clients always go inactive at this point, but a TCP ++ * client may need to stay active and return to READY ++ * state if no other clients are available to listen ++ * for TCP requests on this interface. + * +- * - If a pipeline group is active, detach from it; +- * if the return code indicates that there no more +- * clients left if this pipeline group, we also detach +- * from the TCP client quota. +- * +- * - Otherwise we don't try to detach, we just set the +- * TCP quota pointer to NULL if it wasn't NULL already. +- * +- * tcpquota_disconnect() will set tcpquota to NULL, either +- * by detaching it or by assignment, depending on the +- * needs of the client. See the comments on that function +- * for further information. ++ * Regardless, if we're going to FREED state, that means ++ * the system is shutting down and we don't need to ++ * retain clients. + */ +- if (client->pipeline_refs == NULL || pipeline_detach(client)) { +- tcpquota_disconnect(client); +- } else { +- client->tcpquota = NULL; +- client->tcpattached = false; ++ if (client->mortal && TCP_CLIENT(client) && ++ client->newstate != NS_CLIENTSTATE_FREED && ++ !ns_g_clienttest && ++ isc_atomic_xadd(&client->interface->ntcpaccepting, 0) == 0) ++ { ++ /* Nobody else is accepting */ ++ client->mortal = false; ++ client->newstate = NS_CLIENTSTATE_READY; ++ } ++ ++ /* ++ * Detach from TCP connection and TCP client quota, ++ * if appropriate. If this is the last reference to ++ * the TCP connection in our pipeline group, the ++ * TCP quota slot will be released. ++ */ ++ if (client->tcpconn) { ++ tcpconn_detach(client); + } + + if (client->tcpsocket != NULL) { + CTRACE("closetcp"); + isc_socket_detach(&client->tcpsocket); +- +- if (client->tcpactive) { +- LOCK(&client->interface->lock); +- INSIST(client->interface->ntcpactive > 0); +- client->interface->ntcpactive--; +- UNLOCK(&client->interface->lock); +- client->tcpactive = false; +- } ++ mark_tcp_active(client, false); + } + + if (client->timerset) { +@@ -567,35 +587,6 @@ exit_check(ns_client_t *client) { + client->peeraddr_valid = false; + + client->state = NS_CLIENTSTATE_READY; +- INSIST(client->recursionquota == NULL); +- +- /* +- * Now the client is ready to accept a new TCP connection +- * or UDP request, but we may have enough clients doing +- * that already. Check whether this client needs to remain +- * active and force it to go inactive if not. +- * +- * UDP clients go inactive at this point, but a TCP client +- * may need to remain active and go into ready state if +- * no other clients are available to listen for TCP +- * requests on this interface or (in the case of pipelined +- * clients) to read for additional messages on the current +- * connection. +- */ +- if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) { +- LOCK(&client->interface->lock); +- if ((client->interface->ntcpaccepting == 0 || +- (client->pipelined && +- client->interface->ntcpactive < 2)) && +- client->newstate != NS_CLIENTSTATE_FREED) +- { +- client->mortal = false; +- client->newstate = NS_CLIENTSTATE_READY; +- } +- UNLOCK(&client->interface->lock); +- } +- +- client->pipelined = false; + + /* + * We don't need the client; send it to the inactive +@@ -630,7 +621,7 @@ exit_check(ns_client_t *client) { + } + + /* Still waiting for accept cancel completion. */ +- if (! (client->naccepts == 0)) { ++ if (client->naccepts > 0) { + return (true); + } + +@@ -641,7 +632,7 @@ exit_check(ns_client_t *client) { + } + + /* Still waiting for recv cancel completion. */ +- if (! (client->nrecvs == 0)) { ++ if (client->nrecvs > 0) { + return (true); + } + +@@ -654,14 +645,7 @@ exit_check(ns_client_t *client) { + INSIST(client->recursionquota == NULL); + if (client->tcplistener != NULL) { + isc_socket_detach(&client->tcplistener); +- +- if (client->tcpactive) { +- LOCK(&client->interface->lock); +- INSIST(client->interface->ntcpactive > 0); +- client->interface->ntcpactive--; +- UNLOCK(&client->interface->lock); +- client->tcpactive = false; +- } ++ mark_tcp_active(client, false); + } + if (client->udpsocket != NULL) { + isc_socket_detach(&client->udpsocket); +@@ -816,7 +800,7 @@ client_start(isc_task_t *task, isc_event_t *event) { + return; + + if (TCP_CLIENT(client)) { +- if (client->pipelined) { ++ if (client->tcpconn != NULL) { + client_read(client); + } else { + client_accept(client); +@@ -2470,6 +2454,7 @@ client_request(isc_task_t *task, isc_event_t *event) { + client->nrecvs--; + } else { + INSIST(TCP_CLIENT(client)); ++ INSIST(client->tcpconn != NULL); + REQUIRE(event->ev_type == DNS_EVENT_TCPMSG); + REQUIRE(event->ev_sender == &client->tcpmsg); + buffer = &client->tcpmsg.buffer; +@@ -2657,17 +2642,19 @@ client_request(isc_task_t *task, isc_event_t *event) { + /* + * Pipeline TCP query processing. + */ +- if (client->message->opcode != dns_opcode_query) { +- client->pipelined = false; ++ if (TCP_CLIENT(client) && ++ client->message->opcode != dns_opcode_query) ++ { ++ client->tcpconn->pipelined = false; + } +- if (TCP_CLIENT(client) && client->pipelined) { ++ if (TCP_CLIENT(client) && client->tcpconn->pipelined) { + /* + * We're pipelining. Replace the client; the +- * the replacement can read the TCP socket looking +- * for new messages and this client can process the ++ * replacement can read the TCP socket looking ++ * for new messages and this one can process the + * current message asynchronously. + * +- * There are now at least three clients using this ++ * There will now be at least three clients using this + * TCP socket - one accepting new connections, + * one reading an existing connection to get new + * messages, and one answering the message already +@@ -2675,7 +2662,7 @@ client_request(isc_task_t *task, isc_event_t *event) { + */ + result = ns_client_replace(client); + if (result != ISC_R_SUCCESS) { +- client->pipelined = false; ++ client->tcpconn->pipelined = false; + } + } + +@@ -3233,10 +3220,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { + client->signer = NULL; + dns_name_init(&client->signername, NULL); + client->mortal = false; +- client->pipelined = false; +- client->pipeline_refs = NULL; +- client->tcpquota = NULL; +- client->tcpattached = false; ++ client->tcpconn = NULL; + client->recursionquota = NULL; + client->interface = NULL; + client->peeraddr_valid = false; +@@ -3341,9 +3325,10 @@ client_read(ns_client_t *client) { + + static void + client_newconn(isc_task_t *task, isc_event_t *event) { ++ isc_result_t result; + ns_client_t *client = event->ev_arg; + isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; +- isc_result_t result; ++ uint32_t old; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN); + REQUIRE(NS_CLIENT_VALID(client)); +@@ -3363,10 +3348,8 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + INSIST(client->naccepts == 1); + client->naccepts--; + +- LOCK(&client->interface->lock); +- INSIST(client->interface->ntcpaccepting > 0); +- client->interface->ntcpaccepting--; +- UNLOCK(&client->interface->lock); ++ old = isc_atomic_xadd(&client->interface->ntcpaccepting, -1); ++ INSIST(old > 0); + + /* + * We must take ownership of the new socket before the exit +@@ -3399,7 +3382,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), + "accept failed: %s", + isc_result_totext(nevent->result)); +- tcpquota_disconnect(client); ++ tcpconn_detach(client); + } + + if (exit_check(client)) +@@ -3437,15 +3420,13 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + * telnetting to port 53 (once per CPU) will + * deny service to legitimate TCP clients. + */ +- client->pipelined = false; + result = ns_client_replace(client); + if (result == ISC_R_SUCCESS && + (ns_g_server->keepresporder == NULL || + !allowed(&netaddr, NULL, NULL, 0, NULL, + ns_g_server->keepresporder))) + { +- pipeline_init(client); +- client->pipelined = true; ++ client->tcpconn->pipelined = true; + } + + client_read(client); +@@ -3462,78 +3443,59 @@ client_accept(ns_client_t *client) { + CTRACE("accept"); + + /* +- * The tcpquota object can only be simultaneously referenced a +- * pre-defined number of times; this is configured by 'tcp-clients' +- * in named.conf. If we can't attach to it here, that means the TCP +- * client quota has been exceeded. ++ * Set up a new TCP connection. This means try to attach to the ++ * TCP client quota (tcp-clients), but fail if we're over quota. + */ +- result = isc_quota_attach(&ns_g_server->tcpquota, +- &client->tcpquota); ++ result = tcpconn_init(client, false); + if (result != ISC_R_SUCCESS) { +- bool exit; ++ bool exit; + +- ns_client_log(client, NS_LOGCATEGORY_CLIENT, +- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1), +- "no more TCP clients: %s", +- isc_result_totext(result)); +- +- /* +- * We have exceeded the system-wide TCP client +- * quota. But, we can't just block this accept +- * in all cases, because if we did, a heavy TCP +- * load on other interfaces might cause this +- * interface to be starved, with no clients able +- * to accept new connections. +- * +- * So, we check here to see if any other clients +- * are already servicing TCP queries on this +- * interface (whether accepting, reading, or +- * processing). If there are at least two +- * (one reading and one processing a request) +- * then it's okay *not* to call accept - we +- * can let this client go inactive and another +- * one will resume accepting when it's done. +- * +- * If there aren't enough active clients on the +- * interface, then we can be a little bit +- * flexible about the quota. We'll allow *one* +- * extra client through to ensure we're listening +- * on every interface. +- * +- * (Note: In practice this means that the real +- * TCP client quota is tcp-clients plus the +- * number of listening interfaces plus 2.) +- */ +- LOCK(&client->interface->lock); +- exit = (client->interface->ntcpactive > 1); +- UNLOCK(&client->interface->lock); ++ ns_client_log(client, NS_LOGCATEGORY_CLIENT, ++ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING, ++ "TCP client quota reached: %s", ++ isc_result_totext(result)); + +- if (exit) { +- client->newstate = NS_CLIENTSTATE_INACTIVE; +- (void)exit_check(client); +- return; +- } ++ /* ++ * We have exceeded the system-wide TCP client quota. But, ++ * we can't just block this accept in all cases, because if ++ * we did, a heavy TCP load on other interfaces might cause ++ * this interface to be starved, with no clients able to ++ * accept new connections. ++ * ++ * So, we check here to see if any other clients are ++ * already servicing TCP queries on this interface (whether ++ * accepting, reading, or processing). If we find at least ++ * one, then it's okay *not* to call accept - we can let this ++ * client go inactive and another will take over when it's ++ * done. ++ * ++ * If there aren't enough active clients on the interface, ++ * then we can be a little bit flexible about the quota. ++ * We'll allow *one* extra client through to ensure we're ++ * listening on every interface; we do this by setting the ++ * 'force' option to tcpconn_init(). ++ * ++ * (Note: In practice this means that the real TCP client ++ * quota is tcp-clients plus the number of listening ++ * interfaces plus 1.) ++ */ ++ exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) > 0); ++ if (exit) { ++ client->newstate = NS_CLIENTSTATE_INACTIVE; ++ (void)exit_check(client); ++ return; ++ } + +- } else { +- client->tcpattached = true; ++ result = tcpconn_init(client, true); ++ RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + + /* +- * By incrementing the interface's ntcpactive counter we signal +- * that there is at least one client servicing TCP queries for the +- * interface. +- * +- * We also make note of the fact in the client itself with the +- * tcpactive flag. This ensures proper accounting by preventing +- * us from accidentally incrementing or decrementing ntcpactive +- * more than once per client object. ++ * If this client was set up using get_client() or get_worker(), ++ * then TCP is already marked active. However, if it was restarted ++ * from exit_check(), it might not be, so we take care of it now. + */ +- if (!client->tcpactive) { +- LOCK(&client->interface->lock); +- client->interface->ntcpactive++; +- UNLOCK(&client->interface->lock); +- client->tcpactive = true; +- } ++ mark_tcp_active(client, true); + + result = isc_socket_accept(client->tcplistener, client->task, + client_newconn, client); +@@ -3549,15 +3511,8 @@ client_accept(ns_client_t *client) { + "isc_socket_accept() failed: %s", + isc_result_totext(result)); + +- tcpquota_disconnect(client); +- +- if (client->tcpactive) { +- LOCK(&client->interface->lock); +- client->interface->ntcpactive--; +- UNLOCK(&client->interface->lock); +- client->tcpactive = false; +- } +- ++ tcpconn_detach(client); ++ mark_tcp_active(client, false); + return; + } + +@@ -3582,9 +3537,7 @@ client_accept(ns_client_t *client) { + * listening for connections itself to prevent the interface + * going dead. + */ +- LOCK(&client->interface->lock); +- client->interface->ntcpaccepting++; +- UNLOCK(&client->interface->lock); ++ isc_atomic_xadd(&client->interface->ntcpaccepting, 1); + } + + static void +@@ -3655,24 +3608,25 @@ ns_client_replace(ns_client_t *client) { + REQUIRE(client->manager != NULL); + + tcp = TCP_CLIENT(client); +- if (tcp && client->pipelined) { ++ if (tcp && client->tcpconn != NULL && client->tcpconn->pipelined) { + result = get_worker(client->manager, client->interface, + client->tcpsocket, client); + } else { + result = get_client(client->manager, client->interface, +- client->dispatch, client, tcp); ++ client->dispatch, tcp); + +- /* +- * The responsibility for listening for new requests is hereby +- * transferred to the new client. Therefore, the old client +- * should refrain from listening for any more requests. +- */ +- client->mortal = true; + } + if (result != ISC_R_SUCCESS) { + return (result); + } + ++ /* ++ * The responsibility for listening for new requests is hereby ++ * transferred to the new client. Therefore, the old client ++ * should refrain from listening for any more requests. ++ */ ++ client->mortal = true; ++ + return (ISC_R_SUCCESS); + } + +@@ -3806,7 +3760,7 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) { + + static isc_result_t + get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, +- dns_dispatch_t *disp, ns_client_t *oldclient, bool tcp) ++ dns_dispatch_t *disp, bool tcp) + { + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *ev; +@@ -3850,15 +3804,7 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + client->dscp = ifp->dscp; + + if (tcp) { +- client->tcpattached = false; +- if (oldclient != NULL) { +- client->tcpattached = oldclient->tcpattached; +- } +- +- LOCK(&client->interface->lock); +- client->interface->ntcpactive++; +- UNLOCK(&client->interface->lock); +- client->tcpactive = true; ++ mark_tcp_active(client, true); + + client->attributes |= NS_CLIENTATTR_TCP; + isc_socket_attach(ifp->tcpsocket, +@@ -3923,16 +3869,14 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock, + ns_interface_attach(ifp, &client->interface); + client->newstate = client->state = NS_CLIENTSTATE_WORKING; + INSIST(client->recursionquota == NULL); +- client->tcpquota = &ns_g_server->tcpquota; +- client->tcpattached = oldclient->tcpattached; + + client->dscp = ifp->dscp; + + client->attributes |= NS_CLIENTATTR_TCP; + client->mortal = true; + +- pipeline_attach(oldclient, client); +- client->pipelined = true; ++ tcpconn_attach(oldclient, client); ++ mark_tcp_active(client, true); + + isc_socket_attach(ifp->tcpsocket, &client->tcplistener); + isc_socket_attach(sock, &client->tcpsocket); +@@ -3940,11 +3884,6 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock, + (void)isc_socket_getpeername(client->tcpsocket, &client->peeraddr); + client->peeraddr_valid = true; + +- LOCK(&client->interface->lock); +- client->interface->ntcpactive++; +- UNLOCK(&client->interface->lock); +- client->tcpactive = true; +- + INSIST(client->tcpmsg_valid == false); + dns_tcpmsg_init(client->mctx, client->tcpsocket, &client->tcpmsg); + client->tcpmsg_valid = true; +@@ -3970,8 +3909,7 @@ ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n, + MTRACE("createclients"); + + for (disp = 0; disp < n; disp++) { +- result = get_client(manager, ifp, ifp->udpdispatch[disp], +- NULL, tcp); ++ result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp); + if (result != ISC_R_SUCCESS) + break; + } +diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h +index e2c40acd28..969ee4c08f 100644 +--- a/bin/named/include/named/client.h ++++ b/bin/named/include/named/client.h +@@ -78,6 +78,13 @@ + *** Types + ***/ + ++/*% reference-counted TCP connection object */ ++typedef struct ns_tcpconn { ++ isc_refcount_t refs; ++ isc_quota_t *tcpquota; ++ bool pipelined; ++} ns_tcpconn_t; ++ + /*% nameserver client structure */ + struct ns_client { + unsigned int magic; +@@ -131,10 +138,7 @@ struct ns_client { + dns_name_t signername; /*%< [T]SIG key name */ + dns_name_t *signer; /*%< NULL if not valid sig */ + bool mortal; /*%< Die after handling request */ +- bool pipelined; /*%< TCP queries not in sequence */ +- isc_refcount_t *pipeline_refs; +- isc_quota_t *tcpquota; +- bool tcpattached; ++ ns_tcpconn_t *tcpconn; + isc_quota_t *recursionquota; + ns_interface_t *interface; + +diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h +index 61b08826a6..3535ef22a8 100644 +--- a/bin/named/include/named/interfacemgr.h ++++ b/bin/named/include/named/interfacemgr.h +@@ -9,8 +9,6 @@ + * information regarding copyright ownership. + */ + +-/* $Id: interfacemgr.h,v 1.35 2011/07/28 23:47:58 tbox Exp $ */ +- + #ifndef NAMED_INTERFACEMGR_H + #define NAMED_INTERFACEMGR_H 1 + +@@ -77,11 +75,11 @@ struct ns_interface { + /*%< UDP dispatchers. */ + isc_socket_t * tcpsocket; /*%< TCP socket. */ + isc_dscp_t dscp; /*%< "listen-on" DSCP value */ +- int ntcpaccepting; /*%< Number of clients ++ int32_t ntcpaccepting; /*%< Number of clients + ready to accept new + TCP connections on this + interface */ +- int ntcpactive; /*%< Number of clients ++ int32_t ntcpactive; /*%< Number of clients + servicing TCP queries + (whether accepting or + connected) */ +diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c +index 955096ef47..d9f6df5802 100644 +--- a/bin/named/interfacemgr.c ++++ b/bin/named/interfacemgr.c +@@ -388,6 +388,7 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, + */ + ifp->ntcpaccepting = 0; + ifp->ntcpactive = 0; ++ + ifp->nudpdispatch = 0; + + ifp->dscp = -1; +diff --git a/lib/isc/include/isc/quota.h b/lib/isc/include/isc/quota.h +index b9bf59877a..36c5830242 100644 +--- a/lib/isc/include/isc/quota.h ++++ b/lib/isc/include/isc/quota.h +@@ -100,6 +100,13 @@ isc_quota_attach(isc_quota_t *quota, isc_quota_t **p); + * quota if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA). + */ + ++isc_result_t ++isc_quota_force(isc_quota_t *quota, isc_quota_t **p); ++/*%< ++ * Like isc_quota_attach, but will attach '*p' to the quota ++ * even if the hard quota has been exceeded. ++ */ ++ + void + isc_quota_detach(isc_quota_t **p); + /*%< +diff --git a/lib/isc/quota.c b/lib/isc/quota.c +index 3ddff0d875..556a61f21d 100644 +--- a/lib/isc/quota.c ++++ b/lib/isc/quota.c +@@ -74,20 +74,39 @@ isc_quota_release(isc_quota_t *quota) { + UNLOCK("a->lock); + } + +-isc_result_t +-isc_quota_attach(isc_quota_t *quota, isc_quota_t **p) +-{ ++static isc_result_t ++doattach(isc_quota_t *quota, isc_quota_t **p, bool force) { + isc_result_t result; +- INSIST(p != NULL && *p == NULL); ++ REQUIRE(p != NULL && *p == NULL); ++ + result = isc_quota_reserve(quota); +- if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) ++ if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) { ++ *p = quota; ++ } else if (result == ISC_R_QUOTA && force) { ++ /* attach anyway */ ++ LOCK("a->lock); ++ quota->used++; ++ UNLOCK("a->lock); ++ + *p = quota; ++ result = ISC_R_SUCCESS; ++ } ++ + return (result); + } + ++isc_result_t ++isc_quota_attach(isc_quota_t *quota, isc_quota_t **p) { ++ return (doattach(quota, p, false)); ++} ++ ++isc_result_t ++isc_quota_force(isc_quota_t *quota, isc_quota_t **p) { ++ return (doattach(quota, p, true)); ++} ++ + void +-isc_quota_detach(isc_quota_t **p) +-{ ++isc_quota_detach(isc_quota_t **p) { + INSIST(p != NULL && *p != NULL); + isc_quota_release(*p); + *p = NULL; +diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in +index a82facec0f..7b9f23d776 100644 +--- a/lib/isc/win32/libisc.def.in ++++ b/lib/isc/win32/libisc.def.in +@@ -519,6 +519,7 @@ isc_portset_removerange + isc_quota_attach + isc_quota_destroy + isc_quota_detach ++isc_quota_force + isc_quota_init + isc_quota_max + isc_quota_release +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0006-restore-allowance-for-tcp-clients-interfaces.patch b/meta/recipes-connectivity/bind/bind/0006-restore-allowance-for-tcp-clients-interfaces.patch new file mode 100644 index 0000000000..3821d18501 --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0006-restore-allowance-for-tcp-clients-interfaces.patch @@ -0,0 +1,80 @@ +Backport patch to fix CVE-2018-5743. + +Ref: +https://security-tracker.debian.org/tracker/CVE-2018-5743 + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/59434b9] + +Signed-off-by: Kai Kang + +From 59434b987e8eb436b08c24e559ee094c4e939daa Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Fri, 5 Apr 2019 16:26:19 -0700 +Subject: [PATCH 6/6] restore allowance for tcp-clients < interfaces + +in the "refactor tcpquota and pipeline refs" commit, the counting +of active interfaces was tightened in such a way that named could +fail to listen on an interface if there were more interfaces than +tcp-clients. when checking the quota to start accepting on an +interface, if the number of active clients was above zero, then +it was presumed that some other client was able to handle accepting +new connections. this, however, ignored the fact that the current client +could be included in that count, so if the quota was already exceeded +before all the interfaces were listening, some interfaces would never +listen. + +we now check whether the current client has been marked active; if so, +then the number of active clients on the interface must be greater +than 1, not 0. + +(cherry picked from commit 0b4e2cd4c3192ba88569dd344f542a8cc43742b5) +(cherry picked from commit d01023aaac35543daffbdf48464e320150235d41) +--- + bin/named/client.c | 8 +++++--- + doc/arm/Bv9ARM-book.xml | 3 ++- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index d826ab32bf..845326abc0 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -3464,8 +3464,9 @@ client_accept(ns_client_t *client) { + * + * So, we check here to see if any other clients are + * already servicing TCP queries on this interface (whether +- * accepting, reading, or processing). If we find at least +- * one, then it's okay *not* to call accept - we can let this ++ * accepting, reading, or processing). If we find that at ++ * least one client other than this one is active, then ++ * it's okay *not* to call accept - we can let this + * client go inactive and another will take over when it's + * done. + * +@@ -3479,7 +3480,8 @@ client_accept(ns_client_t *client) { + * quota is tcp-clients plus the number of listening + * interfaces plus 1.) + */ +- exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) > 0); ++ exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) > ++ (client->tcpactive ? 1 : 0)); + if (exit) { + client->newstate = NS_CLIENTSTATE_INACTIVE; + (void)exit_check(client); +diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml +index 381768d540..9c76d3cd6f 100644 +--- a/doc/arm/Bv9ARM-book.xml ++++ b/doc/arm/Bv9ARM-book.xml +@@ -8493,7 +8493,8 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; + + The number of file descriptors reserved for TCP, stdio, + etc. This needs to be big enough to cover the number of +- interfaces named listens on, tcp-clients as well as ++ interfaces named listens on plus ++ tcp-clients, as well as + to provide room for outgoing TCP queries and incoming zone + transfers. The default is 512. + The minimum value is 128 and the +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind/0007-Replace-atomic-operations-in-bin-named-client.c-with.patch b/meta/recipes-connectivity/bind/bind/0007-Replace-atomic-operations-in-bin-named-client.c-with.patch new file mode 100644 index 0000000000..1a84eca58a --- /dev/null +++ b/meta/recipes-connectivity/bind/bind/0007-Replace-atomic-operations-in-bin-named-client.c-with.patch @@ -0,0 +1,140 @@ +Backport commit to fix compile error on arm caused by commits which are +to fix CVE-2018-5743. + +CVE: CVE-2018-5743 +Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/ef49780] + +Signed-off-by: Kai Kang + +From ef49780d30d3ddc5735cfc32561b678a634fa72f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Wed, 17 Apr 2019 15:22:27 +0200 +Subject: [PATCH] Replace atomic operations in bin/named/client.c with + isc_refcount reference counting + +--- + bin/named/client.c | 18 +++++++----------- + bin/named/include/named/interfacemgr.h | 5 +++-- + bin/named/interfacemgr.c | 7 +++++-- + 3 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index 845326abc0..29fecadca8 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -402,12 +402,10 @@ tcpconn_detach(ns_client_t *client) { + static void + mark_tcp_active(ns_client_t *client, bool active) { + if (active && !client->tcpactive) { +- isc_atomic_xadd(&client->interface->ntcpactive, 1); ++ isc_refcount_increment0(&client->interface->ntcpactive, NULL); + client->tcpactive = active; + } else if (!active && client->tcpactive) { +- uint32_t old = +- isc_atomic_xadd(&client->interface->ntcpactive, -1); +- INSIST(old > 0); ++ isc_refcount_decrement(&client->interface->ntcpactive, NULL); + client->tcpactive = active; + } + } +@@ -554,7 +552,7 @@ exit_check(ns_client_t *client) { + if (client->mortal && TCP_CLIENT(client) && + client->newstate != NS_CLIENTSTATE_FREED && + !ns_g_clienttest && +- isc_atomic_xadd(&client->interface->ntcpaccepting, 0) == 0) ++ isc_refcount_current(&client->interface->ntcpaccepting) == 0) + { + /* Nobody else is accepting */ + client->mortal = false; +@@ -3328,7 +3326,6 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + ns_client_t *client = event->ev_arg; + isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; +- uint32_t old; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN); + REQUIRE(NS_CLIENT_VALID(client)); +@@ -3348,8 +3345,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) { + INSIST(client->naccepts == 1); + client->naccepts--; + +- old = isc_atomic_xadd(&client->interface->ntcpaccepting, -1); +- INSIST(old > 0); ++ isc_refcount_decrement(&client->interface->ntcpaccepting, NULL); + + /* + * We must take ownership of the new socket before the exit +@@ -3480,8 +3476,8 @@ client_accept(ns_client_t *client) { + * quota is tcp-clients plus the number of listening + * interfaces plus 1.) + */ +- exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) > +- (client->tcpactive ? 1 : 0)); ++ exit = (isc_refcount_current(&client->interface->ntcpactive) > ++ (client->tcpactive ? 1U : 0U)); + if (exit) { + client->newstate = NS_CLIENTSTATE_INACTIVE; + (void)exit_check(client); +@@ -3539,7 +3535,7 @@ client_accept(ns_client_t *client) { + * listening for connections itself to prevent the interface + * going dead. + */ +- isc_atomic_xadd(&client->interface->ntcpaccepting, 1); ++ isc_refcount_increment0(&client->interface->ntcpaccepting, NULL); + } + + static void +diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h +index 3535ef22a8..6e10f210fd 100644 +--- a/bin/named/include/named/interfacemgr.h ++++ b/bin/named/include/named/interfacemgr.h +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + + #include + +@@ -75,11 +76,11 @@ struct ns_interface { + /*%< UDP dispatchers. */ + isc_socket_t * tcpsocket; /*%< TCP socket. */ + isc_dscp_t dscp; /*%< "listen-on" DSCP value */ +- int32_t ntcpaccepting; /*%< Number of clients ++ isc_refcount_t ntcpaccepting; /*%< Number of clients + ready to accept new + TCP connections on this + interface */ +- int32_t ntcpactive; /*%< Number of clients ++ isc_refcount_t ntcpactive; /*%< Number of clients + servicing TCP queries + (whether accepting or + connected) */ +diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c +index d9f6df5802..135533be6b 100644 +--- a/bin/named/interfacemgr.c ++++ b/bin/named/interfacemgr.c +@@ -386,8 +386,8 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, + * connections will be handled in parallel even though there is + * only one client initially. + */ +- ifp->ntcpaccepting = 0; +- ifp->ntcpactive = 0; ++ isc_refcount_init(&ifp->ntcpaccepting, 0); ++ isc_refcount_init(&ifp->ntcpactive, 0); + + ifp->nudpdispatch = 0; + +@@ -618,6 +618,9 @@ ns_interface_destroy(ns_interface_t *ifp) { + + ns_interfacemgr_detach(&ifp->mgr); + ++ isc_refcount_destroy(&ifp->ntcpactive); ++ isc_refcount_destroy(&ifp->ntcpaccepting); ++ + ifp->magic = 0; + isc_mem_put(mctx, ifp, sizeof(*ifp)); + } +-- +2.20.1 + diff --git a/meta/recipes-connectivity/bind/bind_9.11.5-P4.bb b/meta/recipes-connectivity/bind/bind_9.11.5-P4.bb index 1355841e6b..4fc0f19875 100644 --- a/meta/recipes-connectivity/bind/bind_9.11.5-P4.bb +++ b/meta/recipes-connectivity/bind/bind_9.11.5-P4.bb @@ -20,6 +20,14 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.gz \ file://0001-configure.in-remove-useless-L-use_openssl-lib.patch \ file://0001-named-lwresd-V-and-start-log-hide-build-options.patch \ file://0001-avoid-start-failure-with-bind-user.patch \ + file://0001-bind-fix-CVE-2019-6471.patch \ + file://0001-fix-enforcement-of-tcp-clients-v1.patch \ + file://0002-tcp-clients-could-still-be-exceeded-v2.patch \ + file://0003-use-reference-counter-for-pipeline-groups-v3.patch \ + file://0004-better-tcpquota-accounting-and-client-mortality-chec.patch \ + file://0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch \ + file://0006-restore-allowance-for-tcp-clients-interfaces.patch \ + file://0007-Replace-atomic-operations-in-bin-named-client.c-with.patch \ " SRC_URI[md5sum] = "8ddab4b61fa4516fe404679c74e37960" -- cgit v1.2.3-54-g00ecf