diff options
Diffstat (limited to 'meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch')
-rw-r--r-- | meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch | 512 |
1 files changed, 0 insertions, 512 deletions
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 deleted file mode 100644 index 034ab13303..0000000000 --- a/meta/recipes-connectivity/bind/bind/0004-better-tcpquota-accounting-and-client-mortality-chec.patch +++ /dev/null @@ -1,512 +0,0 @@ -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 <kai.kang@windriver.com> - -From 2ab8a085b3c666f28f1f9229bd6ecb59915b26c3 Mon Sep 17 00:00:00 2001 -From: Evan Hunt <each@isc.org> -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 - |