diff --git a/SOURCES/glibc-rh1904153-1.patch b/SOURCES/glibc-rh1904153-1.patch new file mode 100644 index 0000000..1469c9b --- /dev/null +++ b/SOURCES/glibc-rh1904153-1.patch @@ -0,0 +1,62 @@ +Partial backport of: + +commit 333221862ecbebde60dd16e7ca17d26444e62f50 +Author: Florian Weimer +Date: Mon Apr 8 11:19:38 2019 +0200 + + resolv: Remove RES_INSECURE1, RES_INSECURE2 + + Always perform the associated security checks. + +The constants and their debug output handling are preserve in this +backport. + +diff --git a/resolv/res_send.c b/resolv/res_send.c +index 705ecb7189d215c2..c9b02cca130bc20d 100644 +--- a/resolv/res_send.c ++++ b/resolv/res_send.c +@@ -1324,31 +1324,25 @@ send_dg(res_state statp, + */ + goto wait; + } +- if (!(statp->options & RES_INSECURE1) && +- !res_ourserver_p(statp, &from)) { +- /* +- * response from wrong server? ignore it. +- * XXX - potential security hazard could +- * be detected here. +- */ +- goto wait; +- } +- if (!(statp->options & RES_INSECURE2) +- && (recvresp1 || !res_queriesmatch(buf, buf + buflen, ++ ++ /* Paranoia check. Due to the connected UDP socket, ++ the kernel has already filtered invalid addresses ++ for us. */ ++ if (!res_ourserver_p(statp, &from)) ++ goto wait; ++ ++ /* Check for the correct header layout and a matching ++ question. */ ++ if ((recvresp1 || !res_queriesmatch(buf, buf + buflen, + *thisansp, + *thisansp + + *thisanssizp)) + && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2, + *thisansp, + *thisansp +- + *thisanssizp))) { +- /* +- * response contains wrong query? ignore it. +- * XXX - potential security hazard could +- * be detected here. +- */ +- goto wait; +- } ++ + *thisanssizp))) ++ goto wait; ++ + if (anhp->rcode == SERVFAIL || + anhp->rcode == NOTIMP || + anhp->rcode == REFUSED) { diff --git a/SOURCES/glibc-rh1904153-2.patch b/SOURCES/glibc-rh1904153-2.patch new file mode 100644 index 0000000..a1e5bc9 --- /dev/null +++ b/SOURCES/glibc-rh1904153-2.patch @@ -0,0 +1,51 @@ +Backport the support/ changes from this commit, to avoid future +conflicts: + +commit 446997ff1433d33452b81dfa9e626b8dccf101a4 +Author: Florian Weimer +Date: Wed Oct 30 17:26:58 2019 +0100 + + resolv: Implement trust-ad option for /etc/resolv.conf [BZ #20358] + + This introduces a concept of trusted name servers, for which the + AD bit is passed through to applications. For untrusted name + servers (the default), the AD bit in responses are cleared, to + provide a safe default. + + This approach is very similar to the one suggested by Pavel Šimerda + in . + + The DNS test framework in support/ is enhanced with support for + setting the AD bit in responses. + + Tested on x86_64-linux-gnu. + + Change-Id: Ibfe0f7c73ea221c35979842c5c3b6ed486495ccc + +diff --git a/support/resolv_test.c b/support/resolv_test.c +index 3f2a09f36f445878..28af227cb5ed901c 100644 +--- a/support/resolv_test.c ++++ b/support/resolv_test.c +@@ -182,6 +182,8 @@ resolv_response_init (struct resolv_response_builder *b, + if (flags.tc) + b->buffer[2] |= 0x02; + b->buffer[3] = 0x80 | flags.rcode; /* Always set RA. */ ++ if (flags.ad) ++ b->buffer[3] |= 0x20; + + /* Fill in the initial section count values. */ + b->buffer[4] = flags.qdcount >> 8; +diff --git a/support/resolv_test.h b/support/resolv_test.h +index 4c2e6c1b417f5fcd..be736aead40cd0cc 100644 +--- a/support/resolv_test.h ++++ b/support/resolv_test.h +@@ -134,6 +134,9 @@ struct resolv_response_flags + /* If true, the TC (truncation) flag will be set. */ + bool tc; + ++ /* If true, the AD (authenticated data) flag will be set. */ ++ bool ad; ++ + /* Initial section count values. Can be used to artificially + increase the counts, for malformed packet testing.*/ + unsigned short qdcount; diff --git a/SOURCES/glibc-rh1904153-3.patch b/SOURCES/glibc-rh1904153-3.patch new file mode 100644 index 0000000..dc3c8ea --- /dev/null +++ b/SOURCES/glibc-rh1904153-3.patch @@ -0,0 +1,293 @@ +commit 873e239a4c3d8ec235c27439c1bdc5bbf8aa1818 +Author: Florian Weimer +Date: Wed Oct 14 10:54:39 2020 +0200 + + support: Provide a way to reorder responses within the DNS test server + +diff --git a/support/Makefile b/support/Makefile +index 3c940aa6a7bdfc99..37d5dcc92a5c6dee 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -35,6 +35,8 @@ libsupport-routines = \ + ignore_stderr \ + next_to_fault \ + oom_error \ ++ resolv_response_context_duplicate \ ++ resolv_response_context_free \ + resolv_test \ + set_fortify_handler \ + support-xfstat \ +diff --git a/support/resolv_response_context_duplicate.c b/support/resolv_response_context_duplicate.c +new file mode 100644 +index 0000000000000000..f9c5c3462ad053ec +--- /dev/null ++++ b/support/resolv_response_context_duplicate.c +@@ -0,0 +1,37 @@ ++/* Duplicate a response context used in DNS resolver tests. ++ Copyright (C) 2020 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++ ++struct resolv_response_context * ++resolv_response_context_duplicate (const struct resolv_response_context *ctx) ++{ ++ struct resolv_response_context *result = xmalloc (sizeof (*result)); ++ memcpy (result, ctx, sizeof (*result)); ++ if (result->client_address != NULL) ++ { ++ result->client_address = xmalloc (result->client_address_length); ++ memcpy (result->client_address, ctx->client_address, ++ result->client_address_length); ++ } ++ result->query_buffer = xmalloc (result->query_length); ++ memcpy (result->query_buffer, ctx->query_buffer, result->query_length); ++ return result; ++} +diff --git a/support/resolv_response_context_free.c b/support/resolv_response_context_free.c +new file mode 100644 +index 0000000000000000..b88c05ffd4acfdd4 +--- /dev/null ++++ b/support/resolv_response_context_free.c +@@ -0,0 +1,28 @@ ++/* Free a response context used in DNS resolver tests. ++ Copyright (C) 2020 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++void ++resolv_response_context_free (struct resolv_response_context *ctx) ++{ ++ free (ctx->query_buffer); ++ free (ctx->client_address); ++ free (ctx); ++} +diff --git a/support/resolv_test.c b/support/resolv_test.c +index 28af227cb5ed901c..8cca4e6cf723de28 100644 +--- a/support/resolv_test.c ++++ b/support/resolv_test.c +@@ -434,9 +434,9 @@ resolv_response_buffer (const struct resolv_response_builder *b) + return result; + } + +-static struct resolv_response_builder * +-response_builder_allocate +- (const unsigned char *query_buffer, size_t query_length) ++struct resolv_response_builder * ++resolv_response_builder_allocate (const unsigned char *query_buffer, ++ size_t query_length) + { + struct resolv_response_builder *b = xmalloc (sizeof (*b)); + memset (b, 0, offsetof (struct resolv_response_builder, buffer)); +@@ -445,8 +445,8 @@ response_builder_allocate + return b; + } + +-static void +-response_builder_free (struct resolv_response_builder *b) ++void ++resolv_response_builder_free (struct resolv_response_builder *b) + { + tdestroy (b->compression_offsets, free); + free (b); +@@ -661,13 +661,17 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index) + + struct resolv_response_context ctx = + { ++ .test = obj, ++ .client_address = &peer, ++ .client_address_length = peerlen, + .query_buffer = query, + .query_length = length, + .server_index = server_index, + .tcp = false, + .edns = qinfo.edns, + }; +- struct resolv_response_builder *b = response_builder_allocate (query, length); ++ struct resolv_response_builder *b ++ = resolv_response_builder_allocate (query, length); + obj->config.response_callback + (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype); + +@@ -684,7 +688,7 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index) + if (b->offset >= 12) + printf ("info: UDP server %d: sending response:" + " %zu bytes, RCODE %d (for %s/%u/%u)\n", +- server_index, b->offset, b->buffer[3] & 0x0f, ++ ctx.server_index, b->offset, b->buffer[3] & 0x0f, + qinfo.qname, qinfo.qclass, qinfo.qtype); + else + printf ("info: UDP server %d: sending response: %zu bytes" +@@ -694,23 +698,31 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index) + if (b->truncate_bytes > 0) + printf ("info: truncated by %u bytes\n", b->truncate_bytes); + } +- size_t to_send = b->offset; +- if (to_send < b->truncate_bytes) +- to_send = 0; +- else +- to_send -= b->truncate_bytes; +- +- /* Ignore most errors here because the other end may have closed +- the socket. */ +- if (sendto (obj->servers[server_index].socket_udp, +- b->buffer, to_send, 0, +- (struct sockaddr *) &peer, peerlen) < 0) +- TEST_VERIFY_EXIT (errno != EBADF); ++ resolv_response_send_udp (&ctx, b); + } +- response_builder_free (b); ++ resolv_response_builder_free (b); + return true; + } + ++void ++resolv_response_send_udp (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b) ++{ ++ TEST_VERIFY_EXIT (!ctx->tcp); ++ size_t to_send = b->offset; ++ if (to_send < b->truncate_bytes) ++ to_send = 0; ++ else ++ to_send -= b->truncate_bytes; ++ ++ /* Ignore most errors here because the other end may have closed ++ the socket. */ ++ if (sendto (ctx->test->servers[ctx->server_index].socket_udp, ++ b->buffer, to_send, 0, ++ ctx->client_address, ctx->client_address_length) < 0) ++ TEST_VERIFY_EXIT (errno != EBADF); ++} ++ + /* UDP thread_callback function. Variant for one thread per + server. */ + static void +@@ -897,14 +909,15 @@ server_thread_tcp_client (void *arg) + + struct resolv_response_context ctx = + { ++ .test = closure->obj, + .query_buffer = query_buffer, + .query_length = query_length, + .server_index = closure->server_index, + .tcp = true, + .edns = qinfo.edns, + }; +- struct resolv_response_builder *b = response_builder_allocate +- (query_buffer, query_length); ++ struct resolv_response_builder *b ++ = resolv_response_builder_allocate (query_buffer, query_length); + closure->obj->config.response_callback + (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype); + +@@ -936,7 +949,7 @@ server_thread_tcp_client (void *arg) + writev_fully (closure->client_socket, buffers, 2); + } + bool close_flag = b->close; +- response_builder_free (b); ++ resolv_response_builder_free (b); + free (query_buffer); + if (close_flag) + break; +diff --git a/support/resolv_test.h b/support/resolv_test.h +index be736aead40cd0cc..ff5571dace92c936 100644 +--- a/support/resolv_test.h ++++ b/support/resolv_test.h +@@ -35,25 +35,36 @@ struct resolv_edns_info + uint16_t payload_size; + }; + ++/* This opaque struct collects information about the resolver testing ++ currently in progress. */ ++struct resolv_test; ++ + /* This struct provides context information when the response callback + specified in struct resolv_redirect_config is invoked. */ + struct resolv_response_context + { +- const unsigned char *query_buffer; ++ struct resolv_test *test; ++ void *client_address; ++ size_t client_address_length; ++ unsigned char *query_buffer; + size_t query_length; + int server_index; + bool tcp; + struct resolv_edns_info edns; + }; + ++/* Produces a deep copy of the context. */ ++struct resolv_response_context * ++ resolv_response_context_duplicate (const struct resolv_response_context *); ++ ++/* Frees the copy. For the context passed to the response function, ++ this happens implicitly. */ ++void resolv_response_context_free (struct resolv_response_context *); ++ + /* This opaque struct is used to construct responses from within the + response callback function. */ + struct resolv_response_builder; + +-/* This opaque struct collects information about the resolver testing +- currently in progress. */ +-struct resolv_test; +- + enum + { + /* Maximum number of test servers supported by the framework. */ +@@ -188,6 +199,22 @@ void resolv_response_close (struct resolv_response_builder *); + /* The size of the response packet built so far. */ + size_t resolv_response_length (const struct resolv_response_builder *); + ++/* Allocates a response builder tied to a specific query packet, ++ starting at QUERY_BUFFER, containing QUERY_LENGTH bytes. */ ++struct resolv_response_builder * ++ resolv_response_builder_allocate (const unsigned char *query_buffer, ++ size_t query_length); ++ ++/* Deallocates a response buffer. */ ++void resolv_response_builder_free (struct resolv_response_builder *); ++ ++/* Sends a UDP response using a specific context. This can be used to ++ reorder or duplicate responses, along with ++ resolv_response_context_duplicate and ++ response_builder_allocate. */ ++void resolv_response_send_udp (const struct resolv_response_context *, ++ struct resolv_response_builder *); ++ + __END_DECLS + + #endif /* SUPPORT_RESOLV_TEST_H */ diff --git a/SOURCES/glibc-rh1904153-4.patch b/SOURCES/glibc-rh1904153-4.patch new file mode 100644 index 0000000..7559a15 --- /dev/null +++ b/SOURCES/glibc-rh1904153-4.patch @@ -0,0 +1,36 @@ +commit 08443b19965f48862b02c2fd7b33a39d66daf2ff +Author: Florian Weimer +Date: Wed Oct 14 10:54:39 2020 +0200 + + support: Provide a way to clear the RA bit in DNS server responses + +diff --git a/support/resolv_test.c b/support/resolv_test.c +index 8cca4e6cf723de28..9323f1d55b0be8f1 100644 +--- a/support/resolv_test.c ++++ b/support/resolv_test.c +@@ -181,7 +181,9 @@ resolv_response_init (struct resolv_response_builder *b, + b->buffer[2] |= b->query_buffer[2] & 0x01; /* Copy the RD bit. */ + if (flags.tc) + b->buffer[2] |= 0x02; +- b->buffer[3] = 0x80 | flags.rcode; /* Always set RA. */ ++ b->buffer[3] = flags.rcode; ++ if (!flags.clear_ra) ++ b->buffer[3] |= 0x80; + if (flags.ad) + b->buffer[3] |= 0x20; + +diff --git a/support/resolv_test.h b/support/resolv_test.h +index ff5571dace92c936..825abb9ff2897a43 100644 +--- a/support/resolv_test.h ++++ b/support/resolv_test.h +@@ -148,6 +148,10 @@ struct resolv_response_flags + /* If true, the AD (authenticated data) flag will be set. */ + bool ad; + ++ /* If true, do not set the RA (recursion available) flag in the ++ response. */ ++ bool clear_ra; ++ + /* Initial section count values. Can be used to artificially + increase the counts, for malformed packet testing.*/ + unsigned short qdcount; diff --git a/SOURCES/glibc-rh1904153-5.patch b/SOURCES/glibc-rh1904153-5.patch new file mode 100644 index 0000000..d5848b6 --- /dev/null +++ b/SOURCES/glibc-rh1904153-5.patch @@ -0,0 +1,442 @@ +commit f1f00c072138af90ae6da180f260111f09afe7a3 +Author: Florian Weimer +Date: Wed Oct 14 10:54:39 2020 +0200 + + resolv: Handle transaction ID collisions in parallel queries (bug 26600) + + If the transaction IDs are equal, the old check attributed both + responses to the first query, not recognizing the second response. + This fixes bug 26600. + +diff --git a/resolv/Makefile b/resolv/Makefile +index 72a0f196506ac489..cee5225f8933f245 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -62,6 +62,11 @@ tests += \ + tst-resolv-search \ + tst-resolv-trailing \ + ++# This test calls __res_context_send directly, which is not exported ++# from libresolv. ++tests-internal += tst-resolv-txnid-collision ++tests-static += tst-resolv-txnid-collision ++ + # These tests need libdl. + ifeq (yes,$(build-shared)) + tests += \ +@@ -202,6 +207,8 @@ $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-trailing: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-threads: \ + $(libdl) $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-txnid-collision: $(objpfx)libresolv.a \ ++ $(static-thread-library) + $(objpfx)tst-resolv-canonname: \ + $(libdl) $(objpfx)libresolv.so $(shared-thread-library) + +diff --git a/resolv/res_send.c b/resolv/res_send.c +index c9b02cca130bc20d..ac19627634281c2f 100644 +--- a/resolv/res_send.c ++++ b/resolv/res_send.c +@@ -1315,15 +1315,6 @@ send_dg(res_state statp, + *terrno = EMSGSIZE; + return close_and_return_error (statp, resplen2); + } +- if ((recvresp1 || hp->id != anhp->id) +- && (recvresp2 || hp2->id != anhp->id)) { +- /* +- * response from old query, ignore it. +- * XXX - potential security hazard could +- * be detected here. +- */ +- goto wait; +- } + + /* Paranoia check. Due to the connected UDP socket, + the kernel has already filtered invalid addresses +@@ -1333,15 +1324,24 @@ send_dg(res_state statp, + + /* Check for the correct header layout and a matching + question. */ +- if ((recvresp1 || !res_queriesmatch(buf, buf + buflen, +- *thisansp, +- *thisansp +- + *thisanssizp)) +- && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2, +- *thisansp, +- *thisansp +- + *thisanssizp))) +- goto wait; ++ int matching_query = 0; /* Default to no matching query. */ ++ if (!recvresp1 ++ && anhp->id == hp->id ++ && res_queriesmatch (buf, buf + buflen, ++ *thisansp, *thisansp + *thisanssizp)) ++ matching_query = 1; ++ if (!recvresp2 ++ && anhp->id == hp2->id ++ && res_queriesmatch (buf2, buf2 + buflen2, ++ *thisansp, *thisansp + *thisanssizp)) ++ matching_query = 2; ++ if (matching_query == 0) ++ /* Spurious UDP packet. Drop it and continue ++ waiting. */ ++ { ++ need_recompute = 1; ++ goto wait; ++ } + + if (anhp->rcode == SERVFAIL || + anhp->rcode == NOTIMP || +@@ -1356,7 +1356,7 @@ send_dg(res_state statp, + /* No data from the first reply. */ + resplen = 0; + /* We are waiting for a possible second reply. */ +- if (hp->id == anhp->id) ++ if (matching_query == 1) + recvresp1 = 1; + else + recvresp2 = 1; +@@ -1387,7 +1387,7 @@ send_dg(res_state statp, + return (1); + } + /* Mark which reply we received. */ +- if (recvresp1 == 0 && hp->id == anhp->id) ++ if (matching_query == 1) + recvresp1 = 1; + else + recvresp2 = 1; +diff --git a/resolv/tst-resolv-txnid-collision.c b/resolv/tst-resolv-txnid-collision.c +new file mode 100644 +index 0000000000000000..611d37362f3e5e89 +--- /dev/null ++++ b/resolv/tst-resolv-txnid-collision.c +@@ -0,0 +1,329 @@ ++/* Test parallel queries with transaction ID collisions. ++ Copyright (C) 2020 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Result of parsing a DNS question name. ++ ++ A question name has the form reorder-N-M-rcode-C.example.net, where ++ N and M are either 0 and 1, corresponding to the reorder member, ++ and C is a number that will be stored in the rcode field. ++ ++ Also see parse_qname below. */ ++struct parsed_qname ++{ ++ /* The DNS response code requested from the first server. The ++ second server always responds with RCODE zero. */ ++ int rcode; ++ ++ /* Indicates whether to perform reordering in the responses from the ++ respective server. */ ++ bool reorder[2]; ++}; ++ ++/* Fills *PARSED based on QNAME. */ ++static void ++parse_qname (struct parsed_qname *parsed, const char *qname) ++{ ++ int reorder0; ++ int reorder1; ++ int rcode; ++ char *suffix; ++ if (sscanf (qname, "reorder-%d-%d.rcode-%d.%ms", ++ &reorder0, &reorder1, &rcode, &suffix) == 4) ++ { ++ if (reorder0 != 0) ++ TEST_COMPARE (reorder0, 1); ++ if (reorder1 != 0) ++ TEST_COMPARE (reorder1, 1); ++ TEST_VERIFY (rcode >= 0 && rcode <= 15); ++ TEST_COMPARE_STRING (suffix, "example.net"); ++ free (suffix); ++ ++ parsed->rcode = rcode; ++ parsed->reorder[0] = reorder0; ++ parsed->reorder[1] = reorder1; ++ } ++ else ++ FAIL_EXIT1 ("unexpected query: %s", qname); ++} ++ ++/* Used to construct a response. The first server responds with an ++ error, the second server succeeds. */ ++static void ++build_response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ struct parsed_qname parsed; ++ parse_qname (&parsed, qname); ++ ++ switch (ctx->server_index) ++ { ++ case 0: ++ { ++ struct resolv_response_flags flags = { 0 }; ++ if (parsed.rcode == 0) ++ /* Simulate a delegation in case a NODATA (RCODE zero) ++ response is requested. */ ++ flags.clear_ra = true; ++ else ++ flags.rcode = parsed.rcode; ++ ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ } ++ break; ++ ++ case 1: ++ { ++ struct resolv_response_flags flags = { 0, }; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ ++ resolv_response_section (b, ns_s_an); ++ resolv_response_open_record (b, qname, qclass, qtype, 0); ++ if (qtype == T_A) ++ { ++ char ipv4[4] = { 192, 0, 2, 1 }; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ } ++ else ++ { ++ char ipv6[16] ++ = { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; ++ resolv_response_add_data (b, &ipv6, sizeof (ipv6)); ++ } ++ resolv_response_close_record (b); ++ } ++ break; ++ } ++} ++ ++/* Used to reorder responses. */ ++struct resolv_response_context *previous_query; ++ ++/* Used to keep track of the queries received. */ ++static int previous_server_index = -1; ++static uint16_t previous_qtype; ++ ++/* For each server, buffer the first query and then send both answers ++ to the second query, reordered if requested. */ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_VERIFY (qtype == T_A || qtype == T_AAAA); ++ if (ctx->server_index != 0) ++ TEST_COMPARE (ctx->server_index, 1); ++ ++ struct parsed_qname parsed; ++ parse_qname (&parsed, qname); ++ ++ if (previous_query == NULL) ++ { ++ /* No buffered query. Record this query and do not send a ++ response. */ ++ TEST_COMPARE (previous_qtype, 0); ++ previous_query = resolv_response_context_duplicate (ctx); ++ previous_qtype = qtype; ++ resolv_response_drop (b); ++ previous_server_index = ctx->server_index; ++ ++ if (test_verbose) ++ printf ("info: buffering first query for: %s\n", qname); ++ } ++ else ++ { ++ TEST_VERIFY (previous_query != 0); ++ TEST_COMPARE (ctx->server_index, previous_server_index); ++ TEST_VERIFY (previous_qtype != qtype); /* Not a duplicate. */ ++ ++ /* If reordering, send a response for this query explicitly, and ++ then skip the implicit send. */ ++ if (parsed.reorder[ctx->server_index]) ++ { ++ if (test_verbose) ++ printf ("info: sending reordered second response for: %s\n", ++ qname); ++ build_response (ctx, b, qname, qclass, qtype); ++ resolv_response_send_udp (ctx, b); ++ resolv_response_drop (b); ++ } ++ ++ /* Build a response for the previous query and send it, thus ++ reordering the two responses. */ ++ { ++ if (test_verbose) ++ printf ("info: sending first response for: %s\n", qname); ++ struct resolv_response_builder *btmp ++ = resolv_response_builder_allocate (previous_query->query_buffer, ++ previous_query->query_length); ++ build_response (ctx, btmp, qname, qclass, previous_qtype); ++ resolv_response_send_udp (ctx, btmp); ++ resolv_response_builder_free (btmp); ++ } ++ ++ /* If not reordering, send the reply as usual. */ ++ if (!parsed.reorder[ctx->server_index]) ++ { ++ if (test_verbose) ++ printf ("info: sending non-reordered second response for: %s\n", ++ qname); ++ build_response (ctx, b, qname, qclass, qtype); ++ } ++ ++ /* Unbuffer the response and prepare for the next query. */ ++ resolv_response_context_free (previous_query); ++ previous_query = NULL; ++ previous_qtype = 0; ++ previous_server_index = -1; ++ } ++} ++ ++/* Runs a query for QNAME and checks for the expected reply. See ++ struct parsed_qname for the expected format for QNAME. */ ++static void ++test_qname (const char *qname, int rcode) ++{ ++ struct resolv_context *ctx = __resolv_context_get (); ++ TEST_VERIFY_EXIT (ctx != NULL); ++ ++ unsigned char q1[512]; ++ int q1len = res_mkquery (QUERY, qname, C_IN, T_A, NULL, 0, NULL, ++ q1, sizeof (q1)); ++ TEST_VERIFY_EXIT (q1len > 12); ++ ++ unsigned char q2[512]; ++ int q2len = res_mkquery (QUERY, qname, C_IN, T_AAAA, NULL, 0, NULL, ++ q2, sizeof (q2)); ++ TEST_VERIFY_EXIT (q2len > 12); ++ ++ /* Produce a transaction ID collision. */ ++ memcpy (q2, q1, 2); ++ ++ unsigned char ans1[512]; ++ unsigned char *ans1p = ans1; ++ unsigned char *ans2p = NULL; ++ int nans2p = 0; ++ int resplen2 = 0; ++ int ans2p_malloced = 0; ++ ++ /* Perform a parallel A/AAAA query. */ ++ int resplen1 = __res_context_send (ctx, q1, q1len, q2, q2len, ++ ans1, sizeof (ans1), &ans1p, ++ &ans2p, &nans2p, ++ &resplen2, &ans2p_malloced); ++ ++ TEST_VERIFY (resplen1 > 12); ++ TEST_VERIFY (resplen2 > 12); ++ if (resplen1 <= 12 || resplen2 <= 12) ++ return; ++ ++ if (rcode == 1 || rcode == 3) ++ { ++ /* Format Error and Name Error responses does not trigger ++ switching to the next server. */ ++ TEST_COMPARE (ans1p[3] & 0x0f, rcode); ++ TEST_COMPARE (ans2p[3] & 0x0f, rcode); ++ return; ++ } ++ ++ /* The response should be successful. */ ++ TEST_COMPARE (ans1p[3] & 0x0f, 0); ++ TEST_COMPARE (ans2p[3] & 0x0f, 0); ++ ++ /* Due to bug 19691, the answer may not be in the slot matching the ++ query. Assume that the AAAA response is the longer one. */ ++ unsigned char *a_answer; ++ int a_answer_length; ++ unsigned char *aaaa_answer; ++ int aaaa_answer_length; ++ if (resplen2 > resplen1) ++ { ++ a_answer = ans1p; ++ a_answer_length = resplen1; ++ aaaa_answer = ans2p; ++ aaaa_answer_length = resplen2; ++ } ++ else ++ { ++ a_answer = ans2p; ++ a_answer_length = resplen2; ++ aaaa_answer = ans1p; ++ aaaa_answer_length = resplen1; ++ } ++ ++ { ++ char *expected = xasprintf ("name: %s\n" ++ "address: 192.0.2.1\n", ++ qname); ++ check_dns_packet (qname, a_answer, a_answer_length, expected); ++ free (expected); ++ } ++ { ++ char *expected = xasprintf ("name: %s\n" ++ "address: 2001:db8::1\n", ++ qname); ++ check_dns_packet (qname, aaaa_answer, aaaa_answer_length, expected); ++ free (expected); ++ } ++ ++ if (ans2p_malloced) ++ free (ans2p); ++ ++ __resolv_context_put (ctx); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *aux = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response, ++ }); ++ ++ for (int rcode = 0; rcode <= 5; ++rcode) ++ for (int do_reorder_0 = 0; do_reorder_0 < 2; ++do_reorder_0) ++ for (int do_reorder_1 = 0; do_reorder_1 < 2; ++do_reorder_1) ++ { ++ char *qname = xasprintf ("reorder-%d-%d.rcode-%d.example.net", ++ do_reorder_0, do_reorder_1, rcode); ++ test_qname (qname, rcode); ++ free (qname); ++ } ++ ++ resolv_test_end (aux); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-rh1904153-6.patch b/SOURCES/glibc-rh1904153-6.patch new file mode 100644 index 0000000..c85bafe --- /dev/null +++ b/SOURCES/glibc-rh1904153-6.patch @@ -0,0 +1,31 @@ +commit b8b53b338f6da91e86d115a39da860cefac736ad +Author: Florian Weimer +Date: Thu Oct 15 12:33:13 2020 +0200 + + resolv: Serialize processing in resolv/tst-resolv-txnid-collision + + When switching name servers, response processing by two server + threads clobbers the global test state. (There is still some + risk that this test is negatively impact by packet drops and + packet reordering, but this applies to many of the resolver tests + and is difficult to avoid.) + + Fixes commit f1f00c072138af90ae6da180f260111f09afe7a3 ("resolv: + Handle transaction ID collisions in parallel queries (bug 26600)"). + +diff --git a/resolv/tst-resolv-txnid-collision.c b/resolv/tst-resolv-txnid-collision.c +index 611d37362f3e5e89..189b76f1268f4e4d 100644 +--- a/resolv/tst-resolv-txnid-collision.c ++++ b/resolv/tst-resolv-txnid-collision.c +@@ -309,6 +309,11 @@ do_test (void) + ((struct resolv_redirect_config) + { + .response_callback = response, ++ ++ /* The response callback use global state (the previous_* ++ variables), and query processing must therefore be ++ serialized. */ ++ .single_thread_udp = true, + }); + + for (int rcode = 0; rcode <= 5; ++rcode) diff --git a/SOURCES/glibc-rh1913750-1.patch b/SOURCES/glibc-rh1913750-1.patch new file mode 100644 index 0000000..d76a028 --- /dev/null +++ b/SOURCES/glibc-rh1913750-1.patch @@ -0,0 +1,97 @@ +commit d3c57027470b78dba79c6d931e4e409b1fecfc80 +Author: Patrick McGehearty +Date: Mon Sep 28 20:11:28 2020 +0000 + + Reversing calculation of __x86_shared_non_temporal_threshold + + The __x86_shared_non_temporal_threshold determines when memcpy on x86 + uses non_temporal stores to avoid pushing other data out of the last + level cache. + + This patch proposes to revert the calculation change made by H.J. Lu's + patch of June 2, 2017. + + H.J. Lu's patch selected a threshold suitable for a single thread + getting maximum performance. It was tuned using the single threaded + large memcpy micro benchmark on an 8 core processor. The last change + changes the threshold from using 3/4 of one thread's share of the + cache to using 3/4 of the entire cache of a multi-threaded system + before switching to non-temporal stores. Multi-threaded systems with + more than a few threads are server-class and typically have many + active threads. If one thread consumes 3/4 of the available cache for + all threads, it will cause other active threads to have data removed + from the cache. Two examples show the range of the effect. John + McCalpin's widely parallel Stream benchmark, which runs in parallel + and fetches data sequentially, saw a 20% slowdown with this patch on + an internal system test of 128 threads. This regression was discovered + when comparing OL8 performance to OL7. An example that compares + normal stores to non-temporal stores may be found at + https://vgatherps.github.io/2018-09-02-nontemporal/. A simple test + shows performance loss of 400 to 500% due to a failure to use + nontemporal stores. These performance losses are most likely to occur + when the system load is heaviest and good performance is critical. + + The tunable x86_non_temporal_threshold can be used to override the + default for the knowledgable user who really wants maximum cache + allocation to a single thread in a multi-threaded system. + The manual entry for the tunable has been expanded to provide + more information about its purpose. + + modified: sysdeps/x86/cacheinfo.c + modified: manual/tunables.texi + +Conflicts: + manual/tunables.texi + (Downstream uses the glibc.tune namespace, upstream uses + glibc.cpu.) + sysdeps/x86/cacheinfo.c + (Downstream does not have rep_movsb_threshold, + x86_rep_stosb_threshold tunables.) + +diff --git a/manual/tunables.texi b/manual/tunables.texi +index 3dc6f9a44592c030..3e1e519dff153b09 100644 +--- a/manual/tunables.texi ++++ b/manual/tunables.texi +@@ -364,7 +364,11 @@ set shared cache size in bytes for use in memory and string routines. + + @deftp Tunable glibc.tune.x86_non_temporal_threshold + The @code{glibc.tune.x86_non_temporal_threshold} tunable allows the user +-to set threshold in bytes for non temporal store. ++to set threshold in bytes for non temporal store. Non temporal stores ++give a hint to the hardware to move data directly to memory without ++displacing other data from the cache. This tunable is used by some ++platforms to determine when to use non temporal stores in operations ++like memmove and memcpy. + + This tunable is specific to i386 and x86-64. + @end deftp +diff --git a/sysdeps/x86/cacheinfo.c b/sysdeps/x86/cacheinfo.c +index b9444ddd52051e05..42b468d0c4885bad 100644 +--- a/sysdeps/x86/cacheinfo.c ++++ b/sysdeps/x86/cacheinfo.c +@@ -778,14 +778,20 @@ intel_bug_no_cache_info: + __x86_shared_cache_size = shared; + } + +- /* The large memcpy micro benchmark in glibc shows that 6 times of +- shared cache size is the approximate value above which non-temporal +- store becomes faster on a 8-core processor. This is the 3/4 of the +- total shared cache size. */ ++ /* The default setting for the non_temporal threshold is 3/4 of one ++ thread's share of the chip's cache. For most Intel and AMD processors ++ with an initial release date between 2017 and 2020, a thread's typical ++ share of the cache is from 500 KBytes to 2 MBytes. Using the 3/4 ++ threshold leaves 125 KBytes to 500 KBytes of the thread's data ++ in cache after a maximum temporal copy, which will maintain ++ in cache a reasonable portion of the thread's stack and other ++ active data. If the threshold is set higher than one thread's ++ share of the cache, it has a substantial risk of negatively ++ impacting the performance of other threads running on the chip. */ + __x86_shared_non_temporal_threshold + = (cpu_features->non_temporal_threshold != 0 + ? cpu_features->non_temporal_threshold +- : __x86_shared_cache_size * threads * 3 / 4); ++ : __x86_shared_cache_size * 3 / 4); + } + + #endif diff --git a/SOURCES/glibc-rh1913750-2.patch b/SOURCES/glibc-rh1913750-2.patch new file mode 100644 index 0000000..c976be6 --- /dev/null +++ b/SOURCES/glibc-rh1913750-2.patch @@ -0,0 +1,64 @@ +commit 8813b2682e4094e43b0cf1634e99619f1b8b2c62 +Author: Sajan Karumanchi +Date: Wed Oct 28 13:05:33 2020 +0530 + + x86: Optimizing memcpy for AMD Zen architecture. + + Modifying the shareable cache '__x86_shared_cache_size', which is a + factor in computing the non-temporal threshold parameter + '__x86_shared_non_temporal_threshold' to optimize memcpy for AMD Zen + architectures. + In the existing implementation, the shareable cache is computed as 'L3 + per thread, L2 per core'. Recomputing this shareable cache as 'L3 per + CCX(Core-Complex)' has brought in performance gains. + As per the large bench variant results, this patch also addresses the + regression problem on AMD Zen architectures. + + Backport of commit 59803e81f96b479c17f583b31eac44b57591a1bf upstream, + with the fix from cb3a749a22a55645dc6a52659eea765300623f98 ("x86: + Restore processing of cache size tunables in init_cacheinfo") applied. + + Reviewed-by: Premachandra Mallappa + Co-Authored-by: Florian Weimer + +Backport is off the release/2.32/master branch upstream, to minimize +conflicts. Adjusted for missing "basic" member in struct cpu_features. + +diff --git a/sysdeps/x86/cacheinfo.c b/sysdeps/x86/cacheinfo.c +index 42b468d0c4885bad..57c36d030a76c8b2 100644 +--- a/sysdeps/x86/cacheinfo.c ++++ b/sysdeps/x86/cacheinfo.c +@@ -722,7 +722,7 @@ intel_bug_no_cache_info: + threads = 1 << ((ecx >> 12) & 0x0f); + } + +- if (threads == 0) ++ if (threads == 0 || cpu_features->family >= 0x17) + { + /* If APIC ID width is not available, use logical + processor count. */ +@@ -737,8 +737,22 @@ intel_bug_no_cache_info: + if (threads > 0) + shared /= threads; + +- /* Account for exclusive L2 and L3 caches. */ +- shared += core; ++ /* Get shared cache per ccx for Zen architectures. */ ++ if (cpu_features->family >= 0x17) ++ { ++ unsigned int eax; ++ ++ /* Get number of threads share the L3 cache in CCX. */ ++ __cpuid_count (0x8000001D, 0x3, eax, ebx, ecx, edx); ++ ++ unsigned int threads_per_ccx = ((eax >> 14) & 0xfff) + 1; ++ shared *= threads_per_ccx; ++ } ++ else ++ { ++ /* Account for exclusive L2 and L3 caches. */ ++ shared += core; ++ } + } + + #ifndef DISABLE_PREFETCHW diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index 40339bb..72ce80e 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -1,6 +1,6 @@ %define glibcsrcdir glibc-2.28 %define glibcversion 2.28 -%define glibcrelease 127%{?dist} +%define glibcrelease 127%{?dist}.2 # Pre-release tarballs are pulled in from git using a command that is # effectively: # @@ -486,6 +486,14 @@ Patch352: glibc-rh1642150-4.patch Patch353: glibc-rh1836867.patch Patch354: glibc-rh1821531-1.patch Patch355: glibc-rh1821531-2.patch +Patch356: glibc-rh1904153-1.patch +Patch357: glibc-rh1904153-2.patch +Patch358: glibc-rh1904153-3.patch +Patch359: glibc-rh1904153-4.patch +Patch360: glibc-rh1904153-5.patch +Patch361: glibc-rh1904153-6.patch +Patch362: glibc-rh1913750-1.patch +Patch363: glibc-rh1913750-2.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2384,6 +2392,12 @@ fi %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared %changelog +* Tue Jan 5 2021 Florian Weimer - 2.28-127.2 +- x86: Update auto-tuning of memcpy non-temporal threshold (#1913750) + +* Mon Dec 7 2020 Florian Weimer - 2.28-127.1 +- resolv: Handle DNS transaction ID collisions (#1904153) + * Tue Jun 09 2020 Carlos O'Donell - 2.28-127 - Improve performance of library strstr() function (#1821531)