diff --git a/.gitignore b/.gitignore index 930f2f5..1d3f0e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ SOURCES/glibc-2.34.tar.xz +SOURCES/glibc-c-utf8-locale-2.patch +SOURCES/glibc-upstream-2.34-373.patch diff --git a/.glibc.metadata b/.glibc.metadata index 8570979..ff3c878 100644 --- a/.glibc.metadata +++ b/.glibc.metadata @@ -1 +1,3 @@ 7c3b8890a6346793b6334cc5f2fea5d437d307b8 SOURCES/glibc-2.34.tar.xz +47cf1a27ae2e86b37e44c49f6bf4630a1adabd9a SOURCES/glibc-c-utf8-locale-2.patch +6022f103e5596ad229f22bc966327d71208f7016 SOURCES/glibc-upstream-2.34-373.patch diff --git a/SOURCES/glibc-RHEL-2425-1.patch b/SOURCES/glibc-RHEL-2425-1.patch new file mode 100644 index 0000000..6e8581c --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-1.patch @@ -0,0 +1,65 @@ +commit 51948fdf0fc0258feca719f6a88cbdcf82f2eafc +Author: Siddhesh Poyarekar +Date: Thu Mar 17 11:16:57 2022 +0530 + + nss: Sort tests and tests-container and put one test per line + + Signed-off-by: Siddhesh Poyarekar + (cherry picked from commit e2f68b54e8052da14680074fc5df03153216f218) + +Conflicts: + nss/Makefile + (reordered tests) + +diff --git a/nss/Makefile b/nss/Makefile +index beb30ac2667fe998..5192db6097457f88 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -56,22 +56,31 @@ extra-objs += $(makedb-modules:=.o) + + tests-static = tst-field + tests-internal = tst-field +-tests = test-netdb test-digits-dots tst-nss-getpwent bug17079 \ +- tst-nss-test1 \ +- tst-nss-test2 \ +- tst-nss-test4 \ +- tst-nss-test5 \ +- tst-nss-test_errno +-xtests = bug-erange +- +-tests-container = \ +- tst-nss-compat1 \ +- tst-nss-test3 \ +- tst-nss-files-hosts-long \ +- tst-nss-db-endpwent \ +- tst-nss-db-endgrent \ +- tst-nss-gai-actions \ +- tst-reload1 tst-reload2 ++ ++tests := \ ++ bug17079 \ ++ test-digits-dots \ ++ test-netdb \ ++ tst-nss-getpwent \ ++ tst-nss-test1 \ ++ tst-nss-test2 \ ++ tst-nss-test4 \ ++ tst-nss-test5 \ ++ tst-nss-test_errno \ ++# tests ++ ++xtests = bug-erange ++ ++tests-container := \ ++ tst-nss-compat1 \ ++ tst-nss-db-endgrent \ ++ tst-nss-db-endpwent \ ++ tst-nss-files-hosts-long \ ++ tst-nss-gai-actions \ ++ tst-nss-test3 \ ++ tst-reload1 \ ++ tst-reload2 \ ++# tests-container + + # Tests which need libdl + ifeq (yes,$(build-shared)) diff --git a/SOURCES/glibc-RHEL-2425-10.patch b/SOURCES/glibc-RHEL-2425-10.patch new file mode 100644 index 0000000..f669307 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-10.patch @@ -0,0 +1,156 @@ +commit 4d59769087f2143f619b4b38bf93590a86f5c806 +Author: Siddhesh Poyarekar +Date: Mon Mar 7 19:48:48 2022 +0530 + + gaih_inet: make gethosts into a function + + The macro is quite a pain to debug, so make gethosts into a function to + make it easier to maintain. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit cfa3bd48cb19a70e4367a9978dbba09d9df27a72) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 145ea6fa381ad14b..6be109d07f7fcce0 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -268,63 +268,54 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, + return true; + } + +-#define gethosts(_family) \ +- { \ +- struct hostent th; \ +- char *localcanon = NULL; \ +- no_data = 0; \ +- while (1) \ +- { \ +- status = DL_CALL_FCT (fct, (name, _family, &th, \ +- tmpbuf->data, tmpbuf->length, \ +- &errno, &h_errno, NULL, &localcanon)); \ +- if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL \ +- || errno != ERANGE) \ +- break; \ +- if (!scratch_buffer_grow (tmpbuf)) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_MEMORY; \ +- goto out; \ +- } \ +- } \ +- if (status == NSS_STATUS_NOTFOUND \ +- || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) \ +- { \ +- if (h_errno == NETDB_INTERNAL) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_SYSTEM; \ +- goto out; \ +- } \ +- if (h_errno == TRY_AGAIN) \ +- no_data = EAI_AGAIN; \ +- else \ +- no_data = h_errno == NO_DATA; \ +- } \ +- else if (status == NSS_STATUS_SUCCESS) \ +- { \ +- if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res)) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_SYSTEM; \ +- goto out; \ +- } \ +- \ +- if (localcanon != NULL && res->canon == NULL) \ +- { \ +- char *canonbuf = __strdup (localcanon); \ +- if (canonbuf == NULL) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_SYSTEM; \ +- goto out; \ +- } \ +- res->canon = canonbuf; \ +- } \ +- } \ +- } ++static int ++gethosts (nss_gethostbyname3_r fct, int family, const char *name, ++ const struct addrinfo *req, struct scratch_buffer *tmpbuf, ++ struct gaih_result *res, enum nss_status *statusp, int *no_datap) ++{ ++ struct hostent th; ++ char *localcanon = NULL; ++ enum nss_status status; ++ ++ *no_datap = 0; ++ while (1) ++ { ++ *statusp = status = DL_CALL_FCT (fct, (name, family, &th, ++ tmpbuf->data, tmpbuf->length, ++ &errno, &h_errno, NULL, ++ &localcanon)); ++ if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL ++ || errno != ERANGE) ++ break; ++ if (!scratch_buffer_grow (tmpbuf)) ++ return -EAI_MEMORY; ++ } ++ if (status == NSS_STATUS_NOTFOUND ++ || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) ++ { ++ if (h_errno == NETDB_INTERNAL) ++ return -EAI_SYSTEM; ++ if (h_errno == TRY_AGAIN) ++ *no_datap = EAI_AGAIN; ++ else ++ *no_datap = h_errno == NO_DATA; ++ } ++ else if (status == NSS_STATUS_SUCCESS) ++ { ++ if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res)) ++ return -EAI_SYSTEM; ++ ++ if (localcanon != NULL && res->canon == NULL) ++ { ++ char *canonbuf = __strdup (localcanon); ++ if (canonbuf == NULL) ++ return -EAI_SYSTEM; ++ res->canon = canonbuf; ++ } ++ } + ++ return 0; ++} + + /* This function is called if a canonical name is requested, but if + the service function did not provide it. It tries to obtain the +@@ -741,7 +732,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req, + if (req->ai_family == AF_INET6 + || req->ai_family == AF_UNSPEC) + { +- gethosts (AF_INET6); ++ if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf, ++ res, &status, &no_data)) != 0) ++ { ++ __resolv_context_put (res_ctx); ++ goto out; ++ } + no_inet6_data = no_data; + inet6_status = status; + } +@@ -753,7 +749,12 @@ get_nss_addresses (const char *name, const struct addrinfo *req, + know we are not going to need them. */ + && ((req->ai_flags & AI_ALL) || !res->got_ipv6))) + { +- gethosts (AF_INET); ++ if ((result = gethosts (fct, AF_INET, name, req, tmpbuf, ++ res, &status, &no_data)) != 0) ++ { ++ __resolv_context_put (res_ctx); ++ goto out; ++ } + + if (req->ai_family == AF_INET) + { diff --git a/SOURCES/glibc-RHEL-2425-11.patch b/SOURCES/glibc-RHEL-2425-11.patch new file mode 100644 index 0000000..18763f1 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-11.patch @@ -0,0 +1,178 @@ +commit 6e3fed9d20d6b7ef4b69dd7cfcdd7bbaf1c9a9cb +Author: Siddhesh Poyarekar +Date: Mon Mar 7 20:24:37 2022 +0530 + + gaih_inet: split loopback lookup into its own function + + Flatten the condition nesting and replace the alloca for RET.AT/ATR with + a single array LOCAL_AT[2]. This gets rid of alloca and alloca + accounting. + + `git diff -b` is probably the best way to view this change since much of + the diff is whitespace changes. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit 657472b2a50f67b12e5bbe5827582c9c2bb82dc3) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 6be109d07f7fcce0..827c43b369836de9 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -1004,6 +1004,32 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req, + return -EAI_NODATA; + } + ++/* Add local address information into RES. RES->AT is assumed to have enough ++ space for two tuples and is zeroed out. */ ++ ++static void ++get_local_addresses (const struct addrinfo *req, struct gaih_result *res) ++{ ++ struct gaih_addrtuple *atr = res->at; ++ if (req->ai_family == AF_UNSPEC) ++ res->at->next = res->at + 1; ++ ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) ++ { ++ res->at->family = AF_INET6; ++ if ((req->ai_flags & AI_PASSIVE) == 0) ++ memcpy (res->at->addr, &in6addr_loopback, sizeof (struct in6_addr)); ++ atr = res->at->next; ++ } ++ ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) ++ { ++ atr->family = AF_INET; ++ if ((req->ai_flags & AI_PASSIVE) == 0) ++ atr->addr[0] = htonl (INADDR_LOOPBACK); ++ } ++} ++ + static int + gaih_inet (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai, +@@ -1014,10 +1040,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + + const char *orig_name = name; + +- /* Reserve stack memory for the scratch buffer in the getaddrinfo +- function. */ +- size_t alloca_used = sizeof (struct scratch_buffer); +- + int rc; + if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0) + return rc; +@@ -1027,76 +1049,51 @@ gaih_inet (const char *name, const struct gaih_service *service, + int result = 0; + + struct gaih_result res = {0}; +- if (name != NULL) ++ struct gaih_addrtuple local_at[2] = {0}; ++ ++ res.at = local_at; ++ ++ if (__glibc_unlikely (name == NULL)) + { +- if (req->ai_flags & AI_IDN) +- { +- char *out; +- result = __idna_to_dns_encoding (name, &out); +- if (result != 0) +- return -result; +- name = out; +- malloc_name = true; +- } ++ get_local_addresses (req, &res); ++ goto process_list; ++ } + +- res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); +- res.at->scopeid = 0; +- res.at->next = NULL; ++ if (req->ai_flags & AI_IDN) ++ { ++ char *out; ++ result = __idna_to_dns_encoding (name, &out); ++ if (result != 0) ++ return -result; ++ name = out; ++ malloc_name = true; ++ } + +- if ((result = text_to_binary_address (name, req, &res)) != 0) +- goto free_and_return; +- else if (res.at != NULL) +- goto process_list; ++ if ((result = text_to_binary_address (name, req, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; + +- if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0) +- goto free_and_return; +- else if (res.at != NULL) +- goto process_list; ++ if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; + + #ifdef USE_NSCD +- if ((result = get_nscd_addresses (name, req, &res)) != 0) +- goto free_and_return; +- else if (res.at != NULL) +- goto process_list; ++ if ((result = get_nscd_addresses (name, req, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; + #endif + +- if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0) +- goto free_and_return; +- else if (res.at != NULL) +- goto process_list; +- +- /* None of the lookups worked, so name not found. */ +- result = -EAI_NONAME; +- goto free_and_return; +- } +- else +- { +- struct gaih_addrtuple *atr; +- atr = res.at = alloca_account (sizeof (struct gaih_addrtuple), +- alloca_used); +- memset (res.at, '\0', sizeof (struct gaih_addrtuple)); +- +- if (req->ai_family == AF_UNSPEC) +- { +- res.at->next = __alloca (sizeof (struct gaih_addrtuple)); +- memset (res.at->next, '\0', sizeof (struct gaih_addrtuple)); +- } +- +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) +- { +- res.at->family = AF_INET6; +- if ((req->ai_flags & AI_PASSIVE) == 0) +- memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr)); +- atr = res.at->next; +- } ++ if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; + +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) +- { +- atr->family = AF_INET; +- if ((req->ai_flags & AI_PASSIVE) == 0) +- atr->addr[0] = htonl (INADDR_LOOPBACK); +- } +- } ++ /* None of the lookups worked, so name not found. */ ++ result = -EAI_NONAME; ++ goto free_and_return; + + process_list: + { diff --git a/SOURCES/glibc-RHEL-2425-12.patch b/SOURCES/glibc-RHEL-2425-12.patch new file mode 100644 index 0000000..4b7d019 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-12.patch @@ -0,0 +1,208 @@ +commit 92478a808f477480adbc5ca3d9a4a1bc27fc13ae +Author: Siddhesh Poyarekar +Date: Mon Mar 7 20:38:31 2022 +0530 + + gaih_inet: Split result generation into its own function + + Simplify the loop a wee bit and clean up variable names too. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit ac4653ef503d1e87893d1a6714748a1cdf4bf7ad) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 827c43b369836de9..1008f247365ea009 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -1030,6 +1030,87 @@ get_local_addresses (const struct addrinfo *req, struct gaih_result *res) + } + } + ++/* Generate results in PAI and its count in NADDRS. Return 0 on success or an ++ error code on failure. */ ++ ++static int ++generate_addrinfo (const struct addrinfo *req, struct gaih_result *res, ++ const struct gaih_servtuple *st, struct addrinfo **pai, ++ unsigned int *naddrs) ++{ ++ size_t socklen; ++ sa_family_t family; ++ ++ /* Buffer is the size of an unformatted IPv6 address in printable format. */ ++ for (struct gaih_addrtuple *at = res->at; at != NULL; at = at->next) ++ { ++ family = at->family; ++ if (family == AF_INET6) ++ { ++ socklen = sizeof (struct sockaddr_in6); ++ ++ /* If we looked up IPv4 mapped address discard them here if ++ the caller isn't interested in all address and we have ++ found at least one IPv6 address. */ ++ if (res->got_ipv6 ++ && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED ++ && IN6_IS_ADDR_V4MAPPED (at->addr)) ++ continue; ++ } ++ else ++ socklen = sizeof (struct sockaddr_in); ++ ++ for (int i = 0; st[i].set; i++) ++ { ++ struct addrinfo *ai; ++ ai = *pai = malloc (sizeof (struct addrinfo) + socklen); ++ if (ai == NULL) ++ return -EAI_MEMORY; ++ ++ ai->ai_flags = req->ai_flags; ++ ai->ai_family = family; ++ ai->ai_socktype = st[i].socktype; ++ ai->ai_protocol = st[i].protocol; ++ ai->ai_addrlen = socklen; ++ ai->ai_addr = (void *) (ai + 1); ++ ++ /* We only add the canonical name once. */ ++ ai->ai_canonname = res->canon; ++ res->canon = NULL; ++ ++#ifdef _HAVE_SA_LEN ++ ai->ai_addr->sa_len = socklen; ++#endif /* _HAVE_SA_LEN */ ++ ai->ai_addr->sa_family = family; ++ ++ /* In case of an allocation error the list must be NULL ++ terminated. */ ++ ai->ai_next = NULL; ++ ++ if (family == AF_INET6) ++ { ++ struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) ai->ai_addr; ++ sin6p->sin6_port = st[i].port; ++ sin6p->sin6_flowinfo = 0; ++ memcpy (&sin6p->sin6_addr, at->addr, sizeof (struct in6_addr)); ++ sin6p->sin6_scope_id = at->scopeid; ++ } ++ else ++ { ++ struct sockaddr_in *sinp = (struct sockaddr_in *) ai->ai_addr; ++ sinp->sin_port = st[i].port; ++ memcpy (&sinp->sin_addr, at->addr, sizeof (struct in_addr)); ++ memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); ++ } ++ ++ pai = &(ai->ai_next); ++ } ++ ++ ++*naddrs; ++ } ++ return 0; ++} ++ + static int + gaih_inet (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai, +@@ -1096,98 +1177,13 @@ gaih_inet (const char *name, const struct gaih_service *service, + goto free_and_return; + + process_list: +- { +- /* Set up the canonical name if we need it. */ +- if ((result = process_canonname (req, orig_name, &res)) != 0) +- goto free_and_return; +- +- struct gaih_addrtuple *at2 = res.at; +- size_t socklen; +- sa_family_t family; +- +- /* +- buffer is the size of an unformatted IPv6 address in printable format. +- */ +- while (at2 != NULL) +- { +- family = at2->family; +- if (family == AF_INET6) +- { +- socklen = sizeof (struct sockaddr_in6); +- +- /* If we looked up IPv4 mapped address discard them here if +- the caller isn't interested in all address and we have +- found at least one IPv6 address. */ +- if (res.got_ipv6 +- && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED +- && IN6_IS_ADDR_V4MAPPED (at2->addr)) +- goto ignore; +- } +- else +- socklen = sizeof (struct sockaddr_in); +- +- for (int i = 0; st[i].set; i++) +- { +- struct addrinfo *ai; +- ai = *pai = malloc (sizeof (struct addrinfo) + socklen); +- if (ai == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- +- ai->ai_flags = req->ai_flags; +- ai->ai_family = family; +- ai->ai_socktype = st[i].socktype; +- ai->ai_protocol = st[i].protocol; +- ai->ai_addrlen = socklen; +- ai->ai_addr = (void *) (ai + 1); +- +- /* We only add the canonical name once. */ +- ai->ai_canonname = res.canon; +- res.canon = NULL; +- +-#ifdef _HAVE_SA_LEN +- ai->ai_addr->sa_len = socklen; +-#endif /* _HAVE_SA_LEN */ +- ai->ai_addr->sa_family = family; +- +- /* In case of an allocation error the list must be NULL +- terminated. */ +- ai->ai_next = NULL; +- +- if (family == AF_INET6) +- { +- struct sockaddr_in6 *sin6p = +- (struct sockaddr_in6 *) ai->ai_addr; +- +- sin6p->sin6_port = st[i].port; +- sin6p->sin6_flowinfo = 0; +- memcpy (&sin6p->sin6_addr, +- at2->addr, sizeof (struct in6_addr)); +- sin6p->sin6_scope_id = at2->scopeid; +- } +- else +- { +- struct sockaddr_in *sinp = +- (struct sockaddr_in *) ai->ai_addr; +- sinp->sin_port = st[i].port; +- memcpy (&sinp->sin_addr, +- at2->addr, sizeof (struct in_addr)); +- memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); +- } +- +- pai = &(ai->ai_next); +- } +- +- ++*naddrs; ++ /* Set up the canonical name if we need it. */ ++ if ((result = process_canonname (req, orig_name, &res)) != 0) ++ goto free_and_return; + +- ignore: +- at2 = at2->next; +- } +- } ++ result = generate_addrinfo (req, &res, st, pai, naddrs); + +- free_and_return: ++free_and_return: + if (malloc_name) + free ((char *) name); + free (addrmem); diff --git a/SOURCES/glibc-RHEL-2425-13.patch b/SOURCES/glibc-RHEL-2425-13.patch new file mode 100644 index 0000000..7e31ade --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-13.patch @@ -0,0 +1,34 @@ +commit cc4544ef8069a14c67a46b7e8e28eff1dc102050 +Author: Siddhesh Poyarekar +Date: Wed Mar 2 11:45:29 2022 +0530 + + gethosts: Return EAI_MEMORY on allocation failure + + All other cases of failures due to lack of memory return EAI_MEMORY, so + it seems wrong to return EAI_SYSTEM here. The only reason + convert_hostent_to_gaih_addrtuple could fail is on calloc failure. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit b587456c0e7b59dcfdbd2d44db000a3bc8244e57) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 1008f247365ea009..37260d6e6f292186 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -303,13 +303,13 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name, + else if (status == NSS_STATUS_SUCCESS) + { + if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res)) +- return -EAI_SYSTEM; ++ return -EAI_MEMORY; + + if (localcanon != NULL && res->canon == NULL) + { + char *canonbuf = __strdup (localcanon); + if (canonbuf == NULL) +- return -EAI_SYSTEM; ++ return -EAI_MEMORY; + res->canon = canonbuf; + } + } diff --git a/SOURCES/glibc-RHEL-2425-14.patch b/SOURCES/glibc-RHEL-2425-14.patch new file mode 100644 index 0000000..8ef60c6 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-14.patch @@ -0,0 +1,316 @@ +commit e09ee267c03e3150c2c9ba28625ab130705a485e +Author: Siddhesh Poyarekar +Date: Fri Sep 15 13:51:12 2023 -0400 + + getaddrinfo: Fix use after free in getcanonname (CVE-2023-4806) + + When an NSS plugin only implements the _gethostbyname2_r and + _getcanonname_r callbacks, getaddrinfo could use memory that was freed + during tmpbuf resizing, through h_name in a previous query response. + + The backing store for res->at->name when doing a query with + gethostbyname3_r or gethostbyname2_r is tmpbuf, which is reallocated in + gethosts during the query. For AF_INET6 lookup with AI_ALL | + AI_V4MAPPED, gethosts gets called twice, once for a v6 lookup and second + for a v4 lookup. In this case, if the first call reallocates tmpbuf + enough number of times, resulting in a malloc, th->h_name (that + res->at->name refers to) ends up on a heap allocated storage in tmpbuf. + Now if the second call to gethosts also causes the plugin callback to + return NSS_STATUS_TRYAGAIN, tmpbuf will get freed, resulting in a UAF + reference in res->at->name. This then gets dereferenced in the + getcanonname_r plugin call, resulting in the use after free. + + Fix this by copying h_name over and freeing it at the end. This + resolves BZ #30843, which is assigned CVE-2023-4806. + + Signed-off-by: Siddhesh Poyarekar + (cherry picked from commit 973fe93a5675c42798b2161c6f29c01b0e243994) + +diff --git a/nss/Makefile b/nss/Makefile +index 5192db6097457f88..18a27d79d26f094a 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -80,6 +80,7 @@ tests-container := \ + tst-nss-test3 \ + tst-reload1 \ + tst-reload2 \ ++ tst-nss-gai-hv2-canonname \ + # tests-container + + # Tests which need libdl +@@ -143,7 +144,8 @@ libnss_compat-inhibit-o = $(filter-out .os,$(object-suffixes)) + ifeq ($(build-static-nss),yes) + tests-static += tst-nss-static + endif +-extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os ++extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os \ ++ nss_test_gai_hv2_canonname.os + + include ../Rules + +@@ -178,12 +180,16 @@ rtld-tests-LDFLAGS += -Wl,--dynamic-list=nss_test.ver + libof-nss_test1 = extramodules + libof-nss_test2 = extramodules + libof-nss_test_errno = extramodules ++libof-nss_test_gai_hv2_canonname = extramodules + $(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(link-libc-deps) + $(build-module) + $(objpfx)/libnss_test2.so: $(objpfx)nss_test2.os $(link-libc-deps) + $(build-module) + $(objpfx)/libnss_test_errno.so: $(objpfx)nss_test_errno.os $(link-libc-deps) + $(build-module) ++$(objpfx)/libnss_test_gai_hv2_canonname.so: \ ++ $(objpfx)nss_test_gai_hv2_canonname.os $(link-libc-deps) ++ $(build-module) + $(objpfx)nss_test2.os : nss_test1.c + # Use the nss_files suffix for these objects as well. + $(objpfx)/libnss_test1.so$(libnss_files.so-version): $(objpfx)/libnss_test1.so +@@ -193,10 +199,14 @@ $(objpfx)/libnss_test2.so$(libnss_files.so-version): $(objpfx)/libnss_test2.so + $(objpfx)/libnss_test_errno.so$(libnss_files.so-version): \ + $(objpfx)/libnss_test_errno.so + $(make-link) ++$(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version): \ ++ $(objpfx)/libnss_test_gai_hv2_canonname.so ++ $(make-link) + $(patsubst %,$(objpfx)%.out,$(tests) $(tests-container)) : \ + $(objpfx)/libnss_test1.so$(libnss_files.so-version) \ + $(objpfx)/libnss_test2.so$(libnss_files.so-version) \ +- $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) ++ $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) \ ++ $(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version) + + ifeq (yes,$(have-thread-library)) + $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library) +diff --git a/nss/nss_test_gai_hv2_canonname.c b/nss/nss_test_gai_hv2_canonname.c +new file mode 100644 +index 0000000000000000..4439c83c9f40cf43 +--- /dev/null ++++ b/nss/nss_test_gai_hv2_canonname.c +@@ -0,0 +1,56 @@ ++/* NSS service provider that only provides gethostbyname2_r. ++ Copyright The GNU Toolchain Authors. ++ 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 "nss/tst-nss-gai-hv2-canonname.h" ++ ++/* Catch misnamed and functions. */ ++#pragma GCC diagnostic error "-Wmissing-prototypes" ++NSS_DECLARE_MODULE_FUNCTIONS (test_gai_hv2_canonname) ++ ++extern enum nss_status _nss_files_gethostbyname2_r (const char *, int, ++ struct hostent *, char *, ++ size_t, int *, int *); ++ ++enum nss_status ++_nss_test_gai_hv2_canonname_gethostbyname2_r (const char *name, int af, ++ struct hostent *result, ++ char *buffer, size_t buflen, ++ int *errnop, int *herrnop) ++{ ++ return _nss_files_gethostbyname2_r (name, af, result, buffer, buflen, errnop, ++ herrnop); ++} ++ ++enum nss_status ++_nss_test_gai_hv2_canonname_getcanonname_r (const char *name, char *buffer, ++ size_t buflen, char **result, ++ int *errnop, int *h_errnop) ++{ ++ /* We expect QUERYNAME, which is a small enough string that it shouldn't fail ++ the test. */ ++ if (memcmp (QUERYNAME, name, sizeof (QUERYNAME)) ++ || buflen < sizeof (QUERYNAME)) ++ abort (); ++ ++ strncpy (buffer, name, buflen); ++ *result = buffer; ++ return NSS_STATUS_SUCCESS; ++} +diff --git a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c +new file mode 100644 +index 0000000000000000..d5f10c07d6a90773 +--- /dev/null ++++ b/nss/tst-nss-gai-hv2-canonname.c +@@ -0,0 +1,63 @@ ++/* Test NSS query path for plugins that only implement gethostbyname2 ++ (#30843). ++ Copyright The GNU Toolchain Authors. ++ 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 "nss/tst-nss-gai-hv2-canonname.h" ++ ++#define PREPARE do_prepare ++ ++static void do_prepare (int a, char **av) ++{ ++ FILE *hosts = xfopen ("/etc/hosts", "w"); ++ for (unsigned i = 2; i < 255; i++) ++ { ++ fprintf (hosts, "ff01::ff02:ff03:%u:2\ttest.example.com\n", i); ++ fprintf (hosts, "192.168.0.%u\ttest.example.com\n", i); ++ } ++ xfclose (hosts); ++} ++ ++static int ++do_test (void) ++{ ++ __nss_configure_lookup ("hosts", "test_gai_hv2_canonname"); ++ ++ struct addrinfo hints = {}; ++ struct addrinfo *result = NULL; ++ ++ hints.ai_family = AF_INET6; ++ hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_CANONNAME; ++ ++ int ret = getaddrinfo (QUERYNAME, NULL, &hints, &result); ++ ++ if (ret != 0) ++ FAIL_EXIT1 ("getaddrinfo failed: %s\n", gai_strerror (ret)); ++ ++ TEST_COMPARE_STRING (result->ai_canonname, QUERYNAME); ++ ++ freeaddrinfo(result); ++ return 0; ++} ++ ++#include +diff --git a/nss/tst-nss-gai-hv2-canonname.h b/nss/tst-nss-gai-hv2-canonname.h +new file mode 100644 +index 0000000000000000..14f2a9cb0867dff9 +--- /dev/null ++++ b/nss/tst-nss-gai-hv2-canonname.h +@@ -0,0 +1 @@ ++#define QUERYNAME "test.example.com" +diff --git a/nss/tst-nss-gai-hv2-canonname.root/postclean.req b/nss/tst-nss-gai-hv2-canonname.root/postclean.req +new file mode 100644 +index 0000000000000000..e69de29bb2d1d643 +diff --git a/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script +new file mode 100644 +index 0000000000000000..31848b4a28524af6 +--- /dev/null ++++ b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script +@@ -0,0 +1,2 @@ ++cp $B/nss/libnss_test_gai_hv2_canonname.so $L/libnss_test_gai_hv2_canonname.so.2 ++su +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 37260d6e6f292186..10dc63542f337693 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -120,6 +120,7 @@ struct gaih_result + { + struct gaih_addrtuple *at; + char *canon; ++ char *h_name; + bool free_at; + bool got_ipv6; + }; +@@ -165,6 +166,7 @@ gaih_result_reset (struct gaih_result *res) + if (res->free_at) + free (res->at); + free (res->canon); ++ free (res->h_name); + memset (res, 0, sizeof (*res)); + } + +@@ -203,9 +205,8 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, + return 0; + } + +-/* Convert struct hostent to a list of struct gaih_addrtuple objects. h_name +- is not copied, and the struct hostent object must not be deallocated +- prematurely. The new addresses are appended to the tuple array in RES. */ ++/* Convert struct hostent to a list of struct gaih_addrtuple objects. The new ++ addresses are appended to the tuple array in RES. */ + static bool + convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, + struct hostent *h, struct gaih_result *res) +@@ -238,6 +239,15 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, + res->at = array; + res->free_at = true; + ++ /* Duplicate h_name because it may get reclaimed when the underlying storage ++ is freed. */ ++ if (res->h_name == NULL) ++ { ++ res->h_name = __strdup (h->h_name); ++ if (res->h_name == NULL) ++ return false; ++ } ++ + /* Update the next pointers on reallocation. */ + for (size_t i = 0; i < old; i++) + array[i].next = array + i + 1; +@@ -262,7 +272,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, + } + array[i].next = array + i + 1; + } +- array[0].name = h->h_name; + array[count - 1].next = NULL; + + return true; +@@ -324,15 +333,15 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name, + memory allocation failure. The returned string is allocated on the + heap; the caller has to free it. */ + static char * +-getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name) ++getcanonname (nss_action_list nip, const char *hname, const char *name) + { + nss_getcanonname_r *cfct = __nss_lookup_function (nip, "getcanonname_r"); + char *s = (char *) name; + if (cfct != NULL) + { + char buf[256]; +- if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf), +- &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS) ++ if (DL_CALL_FCT (cfct, (hname ?: name, buf, sizeof (buf), &s, &errno, ++ &h_errno)) != NSS_STATUS_SUCCESS) + /* If the canonical name cannot be determined, use the passed + string. */ + s = (char *) name; +@@ -771,7 +780,7 @@ get_nss_addresses (const char *name, const struct addrinfo *req, + if ((req->ai_flags & AI_CANONNAME) != 0 + && res->canon == NULL) + { +- char *canonbuf = getcanonname (nip, res->at, name); ++ char *canonbuf = getcanonname (nip, res->h_name, name); + if (canonbuf == NULL) + { + __resolv_context_put (res_ctx); diff --git a/SOURCES/glibc-RHEL-2425-15.patch b/SOURCES/glibc-RHEL-2425-15.patch new file mode 100644 index 0000000..03db624 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-15.patch @@ -0,0 +1,37 @@ +commit 57e349b1b0df1aee2dcd19dae1f324bde25ff8f0 +Author: H.J. Lu +Date: Wed Dec 8 07:02:27 2021 -0800 + + Disable DT_RUNPATH on NSS tests [BZ #28455] + + The glibc internal NSS functions should always load NSS modules from + the system. For testing purpose, disable DT_RUNPATH on NSS tests so + that the glibc internal NSS functions can load testing NSS modules + via DT_RPATH. + + This partially fixes BZ #28455. + + Reviewed-by: Carlos O'Donell + +Conflicts: + nss/Makefile + (different test backport order) + +diff --git a/nss/Makefile b/nss/Makefile +index 18a27d79d26f094a..648adf4bb7b1ee6a 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -214,3 +214,13 @@ endif + + $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)/libnss_files.so + $(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)/libnss_files.so ++ ++# Disable DT_RUNPATH on NSS tests so that the glibc internal NSS ++# functions can load testing NSS modules via DT_RPATH. ++LDFLAGS-tst-nss-test1 = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test2 = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test3 = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test4 = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test5 = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test_errno = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test_gai_hv2_canonname = -Wl,--disable-new-dtags diff --git a/SOURCES/glibc-RHEL-2425-16.patch b/SOURCES/glibc-RHEL-2425-16.patch new file mode 100644 index 0000000..6aa6f79 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-16.patch @@ -0,0 +1,84 @@ +commit ec6b95c3303c700eb89eebeda2d7264cc184a796 +Author: Romain Geissler +Date: Mon Sep 25 01:21:51 2023 +0100 + + Fix leak in getaddrinfo introduced by the fix for CVE-2023-4806 [BZ #30843] + + This patch fixes a very recently added leak in getaddrinfo. + + Reviewed-by: Siddhesh Poyarekar + +diff --git a/nss/Makefile b/nss/Makefile +index 648adf4bb7b1ee6a..bfdb0fd5a9a90873 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -147,6 +147,15 @@ endif + extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os \ + nss_test_gai_hv2_canonname.os + ++ifeq ($(run-built-tests),yes) ++ifneq (no,$(PERL)) ++tests-special += $(objpfx)mtrace-tst-nss-gai-hv2-canonname.out ++endif ++endif ++ ++generated += mtrace-tst-nss-gai-hv2-canonname.out \ ++ tst-nss-gai-hv2-canonname.mtrace ++ + include ../Rules + + ifeq (yes,$(have-selinux)) +@@ -215,6 +224,17 @@ endif + $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)/libnss_files.so + $(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)/libnss_files.so + ++tst-nss-gai-hv2-canonname-ENV = \ ++ MALLOC_TRACE=$(objpfx)tst-nss-gai-hv2-canonname.mtrace \ ++ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so ++$(objpfx)mtrace-tst-nss-gai-hv2-canonname.out: \ ++ $(objpfx)tst-nss-gai-hv2-canonname.out ++ { test -r $(objpfx)tst-nss-gai-hv2-canonname.mtrace \ ++ || ( echo "tst-nss-gai-hv2-canonname.mtrace does not exist"; exit 77; ) \ ++ && $(common-objpfx)malloc/mtrace \ ++ $(objpfx)tst-nss-gai-hv2-canonname.mtrace; } > $@; \ ++ $(evaluate-test) ++ + # Disable DT_RUNPATH on NSS tests so that the glibc internal NSS + # functions can load testing NSS modules via DT_RPATH. + LDFLAGS-tst-nss-test1 = -Wl,--disable-new-dtags +diff --git a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c +index d5f10c07d6a90773..7db53cf09da8dcb6 100644 +--- a/nss/tst-nss-gai-hv2-canonname.c ++++ b/nss/tst-nss-gai-hv2-canonname.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include "nss/tst-nss-gai-hv2-canonname.h" +@@ -41,6 +42,8 @@ static void do_prepare (int a, char **av) + static int + do_test (void) + { ++ mtrace (); ++ + __nss_configure_lookup ("hosts", "test_gai_hv2_canonname"); + + struct addrinfo hints = {}; +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 10dc63542f337693..d6046a707f1d742a 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -1196,9 +1196,7 @@ free_and_return: + if (malloc_name) + free ((char *) name); + free (addrmem); +- if (res.free_at) +- free (res.at); +- free (res.canon); ++ gaih_result_reset (&res); + + return result; + } diff --git a/SOURCES/glibc-RHEL-2425-2.patch b/SOURCES/glibc-RHEL-2425-2.patch new file mode 100644 index 0000000..5f5b0c4 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-2.patch @@ -0,0 +1,41 @@ +commit 01671608a3bddde369cdd42aed12e1c019b87158 +Author: Siddhesh Poyarekar +Date: Wed Aug 4 02:21:01 2021 +0530 + + gethosts: Remove unused argument _type + + The generated code is unchanged. + + (cherry picked from commit b17e842a60819098d2a203ecc8b8371b7e1d6c65) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index f391dc0a59849aab..702d8a50e0c218d2 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -239,7 +239,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + return true; + } + +-#define gethosts(_family, _type) \ ++#define gethosts(_family) \ + { \ + struct hostent th; \ + char *localcanon = NULL; \ +@@ -864,7 +864,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + if (req->ai_family == AF_INET6 + || req->ai_family == AF_UNSPEC) + { +- gethosts (AF_INET6, struct in6_addr); ++ gethosts (AF_INET6); + no_inet6_data = no_data; + inet6_status = status; + } +@@ -876,7 +876,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + know we are not going to need them. */ + && ((req->ai_flags & AI_ALL) || !got_ipv6))) + { +- gethosts (AF_INET, struct in_addr); ++ gethosts (AF_INET); + + if (req->ai_family == AF_INET) + { diff --git a/SOURCES/glibc-RHEL-2425-3.patch b/SOURCES/glibc-RHEL-2425-3.patch new file mode 100644 index 0000000..c431d61 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-3.patch @@ -0,0 +1,248 @@ +commit b195fd86c616b147dad3a63498b79e0dedb4662b +Author: Siddhesh Poyarekar +Date: Mon Mar 7 22:17:36 2022 +0530 + + gaih_inet: Simplify canon name resolution + + Simplify logic for allocation of canon to remove the canonbuf variable; + canon now always points to an allocated block. Also pull the canon name + set into a separate function. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit d01411f6bc61429fc027c38827bf3103b48eef2e) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 702d8a50e0c218d2..5c0d873e1d766099 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -285,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + \ + if (localcanon != NULL && canon == NULL) \ + { \ +- canonbuf = __strdup (localcanon); \ ++ char *canonbuf = __strdup (localcanon); \ + if (canonbuf == NULL) \ + { \ + __resolv_context_put (res_ctx); \ +@@ -323,6 +323,41 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name) + return __strdup (name); + } + ++/* Process looked up canonical name and if necessary, decode to IDNA. Result ++ is a new string written to CANONP and the earlier string is freed. */ ++ ++static int ++process_canonname (const struct addrinfo *req, const char *orig_name, ++ char **canonp) ++{ ++ char *canon = *canonp; ++ ++ if ((req->ai_flags & AI_CANONNAME) != 0) ++ { ++ bool do_idn = req->ai_flags & AI_CANONIDN; ++ if (do_idn) ++ { ++ char *out; ++ int rc = __idna_from_dns_encoding (canon ?: orig_name, &out); ++ if (rc == 0) ++ { ++ free (canon); ++ canon = out; ++ } ++ else if (rc == EAI_IDN_ENCODE) ++ /* Use the punycode name as a fallback. */ ++ do_idn = false; ++ else ++ return -rc; ++ } ++ if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL) ++ return -EAI_MEMORY; ++ } ++ ++ *canonp = canon; ++ return 0; ++} ++ + static int + gaih_inet (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai, +@@ -332,7 +367,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; + struct gaih_addrtuple *at = NULL; + bool got_ipv6 = false; +- const char *canon = NULL; ++ char *canon = NULL; + const char *orig_name = name; + + /* Reserve stack memory for the scratch buffer in the getaddrinfo +@@ -453,7 +488,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + + bool malloc_name = false; + struct gaih_addrtuple *addrmem = NULL; +- char *canonbuf = NULL; + int result = 0; + + if (name != NULL) +@@ -495,7 +529,15 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + + if (req->ai_flags & AI_CANONNAME) +- canon = name; ++ { ++ char *canonbuf = __strdup (name); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto free_and_return; ++ } ++ canon = canonbuf; ++ } + + goto process_list; + } +@@ -545,7 +587,15 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + + if (req->ai_flags & AI_CANONNAME) +- canon = name; ++ { ++ char *canonbuf = __strdup (name); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto free_and_return; ++ } ++ canon = canonbuf; ++ } + + goto process_list; + } +@@ -676,9 +726,9 @@ gaih_inet (const char *name, const struct gaih_service *service, + (*pat)->next = NULL; + if (added_canon || air->canon == NULL) + (*pat)->name = NULL; +- else if (canonbuf == NULL) ++ else if (canon == NULL) + { +- canonbuf = __strdup (air->canon); ++ char *canonbuf = __strdup (air->canon); + if (canonbuf == NULL) + { + result = -EAI_MEMORY; +@@ -748,9 +798,9 @@ gaih_inet (const char *name, const struct gaih_service *service, + /* Always start afresh; continue should discard previous results + and the hosts database does not support merge. */ + at = NULL; +- free (canonbuf); ++ free (canon); + free (addrmem); +- canon = canonbuf = NULL; ++ canon = NULL; + addrmem = NULL; + got_ipv6 = false; + +@@ -805,7 +855,16 @@ gaih_inet (const char *name, const struct gaih_service *service, + no_data = 1; + + if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL) +- canon = at->name; ++ { ++ char *canonbuf = __strdup (at->name); ++ if (canonbuf == NULL) ++ { ++ __resolv_context_put (res_ctx); ++ result = -EAI_MEMORY; ++ goto free_and_return; ++ } ++ canon = canonbuf; ++ } + + struct gaih_addrtuple **pat = &at; + +@@ -893,7 +952,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + if ((req->ai_flags & AI_CANONNAME) != 0 + && canon == NULL) + { +- canonbuf = getcanonname (nip, at, name); ++ char *canonbuf = getcanonname (nip, at, name); + if (canonbuf == NULL) + { + __resolv_context_put (res_ctx); +@@ -1004,6 +1063,10 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + + { ++ /* Set up the canonical name if we need it. */ ++ if ((result = process_canonname (req, orig_name, &canon)) != 0) ++ goto free_and_return; ++ + struct gaih_servtuple *st2; + struct gaih_addrtuple *at2 = at; + size_t socklen; +@@ -1014,48 +1077,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + */ + while (at2 != NULL) + { +- /* Only the first entry gets the canonical name. */ +- if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0) +- { +- if (canon == NULL) +- /* If the canonical name cannot be determined, use +- the passed in string. */ +- canon = orig_name; +- +- bool do_idn = req->ai_flags & AI_CANONIDN; +- if (do_idn) +- { +- char *out; +- int rc = __idna_from_dns_encoding (canon, &out); +- if (rc == 0) +- canon = out; +- else if (rc == EAI_IDN_ENCODE) +- /* Use the punycode name as a fallback. */ +- do_idn = false; +- else +- { +- result = -rc; +- goto free_and_return; +- } +- } +- if (!do_idn) +- { +- if (canonbuf != NULL) +- /* We already allocated the string using malloc, but +- the buffer is now owned by canon. */ +- canonbuf = NULL; +- else +- { +- canon = __strdup (canon); +- if (canon == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- } +- } +- } +- + family = at2->family; + if (family == AF_INET6) + { +@@ -1078,7 +1099,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + ai = *pai = malloc (sizeof (struct addrinfo) + socklen); + if (ai == NULL) + { +- free ((char *) canon); + result = -EAI_MEMORY; + goto free_and_return; + } +@@ -1138,7 +1158,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + if (malloc_name) + free ((char *) name); + free (addrmem); +- free (canonbuf); ++ free (canon); + + return result; + } diff --git a/SOURCES/glibc-RHEL-2425-4.patch b/SOURCES/glibc-RHEL-2425-4.patch new file mode 100644 index 0000000..27582f2 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-4.patch @@ -0,0 +1,86 @@ +commit f7efb43738f255db32cfa4e84a491c09f6da66e2 +Author: Siddhesh Poyarekar +Date: Thu Mar 3 23:07:42 2022 +0530 + + getaddrinfo: Fix leak with AI_ALL [BZ #28852] + + Use realloc in convert_hostent_to_gaih_addrtuple and fix up pointers in + the result list so that a single block is maintained for + hostbyname3_r/hostbyname2_r and freed in gaih_inet. This result is + never merged with any other results, since the hosts database does not + permit merging. + + Resolves BZ #28852. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit 300460460706ce3ffe29a7df8966e68323ec5bf1) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 5c0d873e1d766099..ed70e6cb3944d219 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -189,19 +189,16 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, + return 0; + } + +-/* Convert struct hostent to a list of struct gaih_addrtuple objects. +- h_name is not copied, and the struct hostent object must not be +- deallocated prematurely. *RESULT must be NULL or a pointer to a +- linked-list. The new addresses are appended at the end. */ ++/* Convert struct hostent to a list of struct gaih_addrtuple objects. h_name ++ is not copied, and the struct hostent object must not be deallocated ++ prematurely. The new addresses are appended to the tuple array in ++ RESULT. */ + static bool + convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + int family, + struct hostent *h, + struct gaih_addrtuple **result) + { +- while (*result) +- result = &(*result)->next; +- + /* Count the number of addresses in h->h_addr_list. */ + size_t count = 0; + for (char **p = h->h_addr_list; *p != NULL; ++p) +@@ -212,10 +209,30 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr)) + return true; + +- struct gaih_addrtuple *array = calloc (count, sizeof (*array)); ++ struct gaih_addrtuple *array = *result; ++ size_t old = 0; ++ ++ while (array != NULL) ++ { ++ old++; ++ array = array->next; ++ } ++ ++ array = realloc (*result, (old + count) * sizeof (*array)); ++ + if (array == NULL) + return false; + ++ *result = array; ++ ++ /* Update the next pointers on reallocation. */ ++ for (size_t i = 0; i < old; i++) ++ array[i].next = array + i + 1; ++ ++ array += old; ++ ++ memset (array, 0, count * sizeof (*array)); ++ + for (size_t i = 0; i < count; ++i) + { + if (family == AF_INET && req->ai_family == AF_INET6) +@@ -235,7 +252,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + array[0].name = h->h_name; + array[count - 1].next = NULL; + +- *result = array; + return true; + } + diff --git a/SOURCES/glibc-RHEL-2425-5.patch b/SOURCES/glibc-RHEL-2425-5.patch new file mode 100644 index 0000000..427630d --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-5.patch @@ -0,0 +1,285 @@ +commit e05e5889b8a307fe4be55b03bcbd7a1c62fc2f2d +Author: Siddhesh Poyarekar +Date: Thu Feb 10 13:27:11 2022 +0530 + + gaih_inet: Simplify service resolution + + Refactor the code to split out the service resolution code into a + separate function. Allocate the service tuples array just once to the + size of the typeproto array, thus avoiding the unnecessary pointer + chasing and stack allocations. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit 8d6cf99f2fb81a097f9334c125e5c23604af1a98) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index ed70e6cb3944d219..8c78ef9570fe0f58 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -100,14 +100,12 @@ struct gaih_service + + struct gaih_servtuple + { +- struct gaih_servtuple *next; + int socktype; + int protocol; + int port; ++ bool set; + }; + +-static const struct gaih_servtuple nullserv; +- + + struct gaih_typeproto + { +@@ -180,11 +178,11 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, + } + while (r); + +- st->next = NULL; + st->socktype = tp->socktype; + st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) + ? req->ai_protocol : tp->protocol); + st->port = s->s_port; ++ st->set = true; + + return 0; + } +@@ -375,20 +373,11 @@ process_canonname (const struct addrinfo *req, const char *orig_name, + } + + static int +-gaih_inet (const char *name, const struct gaih_service *service, +- const struct addrinfo *req, struct addrinfo **pai, +- unsigned int *naddrs, struct scratch_buffer *tmpbuf) ++get_servtuples (const struct gaih_service *service, const struct addrinfo *req, ++ struct gaih_servtuple *st, struct scratch_buffer *tmpbuf) + { ++ int i; + const struct gaih_typeproto *tp = gaih_inet_typeproto; +- struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; +- struct gaih_addrtuple *at = NULL; +- bool got_ipv6 = false; +- char *canon = NULL; +- const char *orig_name = name; +- +- /* Reserve stack memory for the scratch buffer in the getaddrinfo +- function. */ +- size_t alloca_used = sizeof (struct scratch_buffer); + + if (req->ai_protocol || req->ai_socktype) + { +@@ -410,98 +399,88 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + } + +- int port = 0; +- if (service != NULL) ++ if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0) ++ return -EAI_SERVICE; ++ ++ if (service == NULL || service->num >= 0) + { +- if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) +- return -EAI_SERVICE; ++ int port = service != NULL ? htons (service->num) : 0; + +- if (service->num < 0) ++ if (req->ai_socktype || req->ai_protocol) + { +- if (tp->name[0]) +- { +- st = (struct gaih_servtuple *) +- alloca_account (sizeof (struct gaih_servtuple), alloca_used); +- +- int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf); +- if (__glibc_unlikely (rc != 0)) +- return rc; +- } +- else +- { +- struct gaih_servtuple **pst = &st; +- for (tp++; tp->name[0]; tp++) +- { +- struct gaih_servtuple *newp; ++ st[0].socktype = tp->socktype; ++ st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) ++ ? req->ai_protocol : tp->protocol); ++ st[0].port = port; ++ st[0].set = true; + +- if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) +- continue; ++ return 0; ++ } + +- if (req->ai_socktype != 0 +- && req->ai_socktype != tp->socktype) +- continue; +- if (req->ai_protocol != 0 +- && !(tp->protoflag & GAI_PROTO_PROTOANY) +- && req->ai_protocol != tp->protocol) +- continue; ++ /* Neither socket type nor protocol is set. Return all socket types ++ we know about. */ ++ for (i = 0, ++tp; tp->name[0]; ++tp) ++ if (tp->defaultflag) ++ { ++ st[i].socktype = tp->socktype; ++ st[i].protocol = tp->protocol; ++ st[i].port = port; ++ st[i++].set = true; ++ } + +- newp = (struct gaih_servtuple *) +- alloca_account (sizeof (struct gaih_servtuple), +- alloca_used); ++ return 0; ++ } + +- if (gaih_inet_serv (service->name, +- tp, req, newp, tmpbuf) != 0) +- continue; ++ if (tp->name[0]) ++ return gaih_inet_serv (service->name, tp, req, st, tmpbuf); + +- *pst = newp; +- pst = &(newp->next); +- } +- if (st == (struct gaih_servtuple *) &nullserv) +- return -EAI_SERVICE; +- } +- } +- else +- { +- port = htons (service->num); +- goto got_port; +- } +- } +- else ++ for (i = 0, tp++; tp->name[0]; tp++) + { +- got_port: ++ if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) ++ continue; + +- if (req->ai_socktype || req->ai_protocol) +- { +- st = alloca_account (sizeof (struct gaih_servtuple), alloca_used); +- st->next = NULL; +- st->socktype = tp->socktype; +- st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) +- ? req->ai_protocol : tp->protocol); +- st->port = port; +- } +- else +- { +- /* Neither socket type nor protocol is set. Return all socket types +- we know about. */ +- struct gaih_servtuple **lastp = &st; +- for (++tp; tp->name[0]; ++tp) +- if (tp->defaultflag) +- { +- struct gaih_servtuple *newp; ++ if (req->ai_socktype != 0 ++ && req->ai_socktype != tp->socktype) ++ continue; ++ if (req->ai_protocol != 0 ++ && !(tp->protoflag & GAI_PROTO_PROTOANY) ++ && req->ai_protocol != tp->protocol) ++ continue; + +- newp = alloca_account (sizeof (struct gaih_servtuple), +- alloca_used); +- newp->next = NULL; +- newp->socktype = tp->socktype; +- newp->protocol = tp->protocol; +- newp->port = port; ++ if (gaih_inet_serv (service->name, ++ tp, req, &st[i], tmpbuf) != 0) ++ continue; + +- *lastp = newp; +- lastp = &newp->next; +- } +- } ++ i++; + } + ++ if (!st[0].set) ++ return -EAI_SERVICE; ++ ++ return 0; ++} ++ ++static int ++gaih_inet (const char *name, const struct gaih_service *service, ++ const struct addrinfo *req, struct addrinfo **pai, ++ unsigned int *naddrs, struct scratch_buffer *tmpbuf) ++{ ++ struct gaih_servtuple st[sizeof (gaih_inet_typeproto) ++ / sizeof (struct gaih_typeproto)] = {0}; ++ ++ struct gaih_addrtuple *at = NULL; ++ bool got_ipv6 = false; ++ char *canon = NULL; ++ const char *orig_name = name; ++ ++ /* Reserve stack memory for the scratch buffer in the getaddrinfo ++ function. */ ++ size_t alloca_used = sizeof (struct scratch_buffer); ++ ++ int rc; ++ if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0) ++ return rc; ++ + bool malloc_name = false; + struct gaih_addrtuple *addrmem = NULL; + int result = 0; +@@ -1083,7 +1062,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + if ((result = process_canonname (req, orig_name, &canon)) != 0) + goto free_and_return; + +- struct gaih_servtuple *st2; + struct gaih_addrtuple *at2 = at; + size_t socklen; + sa_family_t family; +@@ -1109,7 +1087,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + else + socklen = sizeof (struct sockaddr_in); + +- for (st2 = st; st2 != NULL; st2 = st2->next) ++ for (int i = 0; st[i].set; i++) + { + struct addrinfo *ai; + ai = *pai = malloc (sizeof (struct addrinfo) + socklen); +@@ -1121,8 +1099,8 @@ gaih_inet (const char *name, const struct gaih_service *service, + + ai->ai_flags = req->ai_flags; + ai->ai_family = family; +- ai->ai_socktype = st2->socktype; +- ai->ai_protocol = st2->protocol; ++ ai->ai_socktype = st[i].socktype; ++ ai->ai_protocol = st[i].protocol; + ai->ai_addrlen = socklen; + ai->ai_addr = (void *) (ai + 1); + +@@ -1144,7 +1122,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct sockaddr_in6 *sin6p = + (struct sockaddr_in6 *) ai->ai_addr; + +- sin6p->sin6_port = st2->port; ++ sin6p->sin6_port = st[i].port; + sin6p->sin6_flowinfo = 0; + memcpy (&sin6p->sin6_addr, + at2->addr, sizeof (struct in6_addr)); +@@ -1154,7 +1132,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + { + struct sockaddr_in *sinp = + (struct sockaddr_in *) ai->ai_addr; +- sinp->sin_port = st2->port; ++ sinp->sin_port = st[i].port; + memcpy (&sinp->sin_addr, + at2->addr, sizeof (struct in_addr)); + memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); diff --git a/SOURCES/glibc-RHEL-2425-6.patch b/SOURCES/glibc-RHEL-2425-6.patch new file mode 100644 index 0000000..f6425af --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-6.patch @@ -0,0 +1,1123 @@ +commit 922f2614d69dc47922c1a8e8a08f2bd74874587e +Author: Siddhesh Poyarekar +Date: Mon Mar 7 14:08:51 2022 +0530 + + gaih_inet: make numeric lookup a separate routine + + Introduce the gaih_result structure and general paradigm for cleanups + that follow to process the lookup request and return a result. A lookup + function (like text_to_binary_address), should return an integer error + code and set members of gaih_result based on what it finds. If the + function does not have a result and no errors have occurred during the + lookup, it should return 0 and res.at should be set to NULL, allowing a + subsequent function to do the lookup until we run out of options. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit 26dea461191cca519b498890a9682fe4bc8e4c2f) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 8c78ef9570fe0f58..57b6834c8bb3887c 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -116,6 +116,12 @@ struct gaih_typeproto + char name[8]; + }; + ++struct gaih_result ++{ ++ struct gaih_addrtuple *at; ++ char *canon; ++}; ++ + /* Values for `protoflag'. */ + #define GAI_PROTO_NOSERVICE 1 + #define GAI_PROTO_PROTOANY 2 +@@ -297,7 +303,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + } \ + *pat = addrmem; \ + \ +- if (localcanon != NULL && canon == NULL) \ ++ if (localcanon != NULL && res.canon == NULL) \ + { \ + char *canonbuf = __strdup (localcanon); \ + if (canonbuf == NULL) \ +@@ -306,7 +312,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + result = -EAI_SYSTEM; \ + goto free_and_return; \ + } \ +- canon = canonbuf; \ ++ res.canon = canonbuf; \ + } \ + if (_family == AF_INET6 && *pat != NULL) \ + got_ipv6 = true; \ +@@ -342,9 +348,9 @@ getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name) + + static int + process_canonname (const struct addrinfo *req, const char *orig_name, +- char **canonp) ++ struct gaih_result *res) + { +- char *canon = *canonp; ++ char *canon = res->canon; + + if ((req->ai_flags & AI_CANONNAME) != 0) + { +@@ -368,7 +374,7 @@ process_canonname (const struct addrinfo *req, const char *orig_name, + return -EAI_MEMORY; + } + +- *canonp = canon; ++ res->canon = canon; + return 0; + } + +@@ -460,6 +466,105 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req, + return 0; + } + ++/* Convert numeric addresses to binary into RES. On failure, RES->AT is set to ++ NULL and an error code is returned. If AI_NUMERIC_HOST is not requested and ++ the function cannot determine a result, RES->AT is set to NULL and 0 ++ returned. */ ++ ++static int ++text_to_binary_address (const char *name, const struct addrinfo *req, ++ struct gaih_result *res) ++{ ++ struct gaih_addrtuple *at = res->at; ++ int result = 0; ++ ++ assert (at != NULL); ++ ++ memset (at->addr, 0, sizeof (at->addr)); ++ if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0) ++ { ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) ++ at->family = AF_INET; ++ else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) ++ { ++ at->addr[3] = at->addr[0]; ++ at->addr[2] = htonl (0xffff); ++ at->addr[1] = 0; ++ at->addr[0] = 0; ++ at->family = AF_INET6; ++ } ++ else ++ { ++ result = -EAI_ADDRFAMILY; ++ goto out; ++ } ++ ++ if (req->ai_flags & AI_CANONNAME) ++ { ++ char *canonbuf = __strdup (name); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ return 0; ++ } ++ ++ char *scope_delim = strchr (name, SCOPE_DELIMITER); ++ int e; ++ ++ if (scope_delim == NULL) ++ e = inet_pton (AF_INET6, name, at->addr); ++ else ++ e = __inet_pton_length (AF_INET6, name, scope_delim - name, at->addr); ++ ++ if (e > 0) ++ { ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) ++ at->family = AF_INET6; ++ else if (req->ai_family == AF_INET ++ && IN6_IS_ADDR_V4MAPPED (at->addr)) ++ { ++ at->addr[0] = at->addr[3]; ++ at->family = AF_INET; ++ } ++ else ++ { ++ result = -EAI_ADDRFAMILY; ++ goto out; ++ } ++ ++ if (scope_delim != NULL ++ && __inet6_scopeid_pton ((struct in6_addr *) at->addr, ++ scope_delim + 1, &at->scopeid) != 0) ++ { ++ result = -EAI_NONAME; ++ goto out; ++ } ++ ++ if (req->ai_flags & AI_CANONNAME) ++ { ++ char *canonbuf = __strdup (name); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ return 0; ++ } ++ ++ if ((req->ai_flags & AI_NUMERICHOST)) ++ result = -EAI_NONAME; ++ ++out: ++ res->at = NULL; ++ return result; ++} ++ + static int + gaih_inet (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai, +@@ -468,9 +573,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct gaih_servtuple st[sizeof (gaih_inet_typeproto) + / sizeof (struct gaih_typeproto)] = {0}; + +- struct gaih_addrtuple *at = NULL; + bool got_ipv6 = false; +- char *canon = NULL; + const char *orig_name = name; + + /* Reserve stack memory for the scratch buffer in the getaddrinfo +@@ -485,6 +588,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct gaih_addrtuple *addrmem = NULL; + int result = 0; + ++ struct gaih_result res = {0}; + if (name != NULL) + { + if (req->ai_flags & AI_IDN) +@@ -497,533 +601,441 @@ gaih_inet (const char *name, const struct gaih_service *service, + malloc_name = true; + } + +- uint32_t addr[4]; +- if (__inet_aton_exact (name, (struct in_addr *) addr) != 0) ++ res.at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); ++ res.at->scopeid = 0; ++ res.at->next = NULL; ++ ++ if ((result = text_to_binary_address (name, req, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++ ++ int no_data = 0; ++ int no_inet6_data = 0; ++ nss_action_list nip; ++ enum nss_status inet6_status = NSS_STATUS_UNAVAIL; ++ enum nss_status status = NSS_STATUS_UNAVAIL; ++ int no_more; ++ struct resolv_context *res_ctx = NULL; ++ bool do_merge = false; ++ ++ /* If we do not have to look for IPv6 addresses or the canonical ++ name, use the simple, old functions, which do not support ++ IPv6 scope ids, nor retrieving the canonical name. */ ++ if (req->ai_family == AF_INET ++ && (req->ai_flags & AI_CANONNAME) == 0) + { +- at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); +- at->scopeid = 0; +- at->next = NULL; +- +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) +- { +- memcpy (at->addr, addr, sizeof (at->addr)); +- at->family = AF_INET; +- } +- else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) +- { +- at->addr[3] = addr[0]; +- at->addr[2] = htonl (0xffff); +- at->addr[1] = 0; +- at->addr[0] = 0; +- at->family = AF_INET6; +- } +- else +- { +- result = -EAI_ADDRFAMILY; +- goto free_and_return; +- } ++ int rc; ++ struct hostent th; ++ struct hostent *h; + +- if (req->ai_flags & AI_CANONNAME) ++ while (1) + { +- char *canonbuf = __strdup (name); +- if (canonbuf == NULL) ++ rc = __gethostbyname2_r (name, AF_INET, &th, ++ tmpbuf->data, tmpbuf->length, ++ &h, &h_errno); ++ if (rc != ERANGE || h_errno != NETDB_INTERNAL) ++ break; ++ if (!scratch_buffer_grow (tmpbuf)) + { + result = -EAI_MEMORY; + goto free_and_return; + } +- canon = canonbuf; + } + +- goto process_list; +- } +- +- char *scope_delim = strchr (name, SCOPE_DELIMITER); +- int e; +- +- if (scope_delim == NULL) +- e = inet_pton (AF_INET6, name, addr); +- else +- e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr); +- +- if (e > 0) +- { +- at = alloca_account (sizeof (struct gaih_addrtuple), +- alloca_used); +- at->scopeid = 0; +- at->next = NULL; +- +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) +- { +- memcpy (at->addr, addr, sizeof (at->addr)); +- at->family = AF_INET6; +- } +- else if (req->ai_family == AF_INET +- && IN6_IS_ADDR_V4MAPPED (addr)) ++ if (rc == 0) + { +- at->addr[0] = addr[3]; +- at->addr[1] = addr[1]; +- at->addr[2] = addr[2]; +- at->addr[3] = addr[3]; +- at->family = AF_INET; ++ if (h != NULL) ++ { ++ /* We found data, convert it. */ ++ if (!convert_hostent_to_gaih_addrtuple ++ (req, AF_INET, h, &addrmem)) ++ { ++ result = -EAI_MEMORY; ++ goto free_and_return; ++ } ++ res.at = addrmem; ++ } ++ else ++ { ++ if (h_errno == NO_DATA) ++ result = -EAI_NODATA; ++ else ++ result = -EAI_NONAME; ++ goto free_and_return; ++ } + } + else + { +- result = -EAI_ADDRFAMILY; +- goto free_and_return; +- } ++ if (h_errno == NETDB_INTERNAL) ++ result = -EAI_SYSTEM; ++ else if (h_errno == TRY_AGAIN) ++ result = -EAI_AGAIN; ++ else ++ /* We made requests but they turned out no data. ++ The name is known, though. */ ++ result = -EAI_NODATA; + +- if (scope_delim != NULL +- && __inet6_scopeid_pton ((struct in6_addr *) at->addr, +- scope_delim + 1, +- &at->scopeid) != 0) +- { +- result = -EAI_NONAME; + goto free_and_return; + } + +- if (req->ai_flags & AI_CANONNAME) ++ goto process_list; ++ } ++ ++#ifdef USE_NSCD ++ if (__nss_not_use_nscd_hosts > 0 ++ && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) ++ __nss_not_use_nscd_hosts = 0; ++ ++ if (!__nss_not_use_nscd_hosts ++ && !__nss_database_custom[NSS_DBSIDX_hosts]) ++ { ++ /* Try to use nscd. */ ++ struct nscd_ai_result *air = NULL; ++ int err = __nscd_getai (name, &air, &h_errno); ++ if (air != NULL) + { +- char *canonbuf = __strdup (name); +- if (canonbuf == NULL) ++ /* Transform into gaih_addrtuple list. */ ++ bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; ++ char *addrs = air->addrs; ++ ++ addrmem = calloc (air->naddrs, sizeof (*addrmem)); ++ if (addrmem == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } +- canon = canonbuf; +- } + +- goto process_list; +- } +- +- if ((req->ai_flags & AI_NUMERICHOST) == 0) +- { +- int no_data = 0; +- int no_inet6_data = 0; +- nss_action_list nip; +- enum nss_status inet6_status = NSS_STATUS_UNAVAIL; +- enum nss_status status = NSS_STATUS_UNAVAIL; +- int no_more; +- struct resolv_context *res_ctx = NULL; +- bool do_merge = false; +- +- /* If we do not have to look for IPv6 addresses or the canonical +- name, use the simple, old functions, which do not support +- IPv6 scope ids, nor retrieving the canonical name. */ +- if (req->ai_family == AF_INET +- && (req->ai_flags & AI_CANONNAME) == 0) +- { +- int rc; +- struct hostent th; +- struct hostent *h; ++ struct gaih_addrtuple *addrfree = addrmem; ++ struct gaih_addrtuple **pat = &res.at; + +- while (1) ++ for (int i = 0; i < air->naddrs; ++i) + { +- rc = __gethostbyname2_r (name, AF_INET, &th, +- tmpbuf->data, tmpbuf->length, +- &h, &h_errno); +- if (rc != ERANGE || h_errno != NETDB_INTERNAL) +- break; +- if (!scratch_buffer_grow (tmpbuf)) ++ socklen_t size = (air->family[i] == AF_INET ++ ? INADDRSZ : IN6ADDRSZ); ++ ++ if (!((air->family[i] == AF_INET ++ && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) != 0) ++ || req->ai_family == AF_UNSPEC ++ || air->family[i] == req->ai_family)) + { +- result = -EAI_MEMORY; +- goto free_and_return; ++ /* Skip over non-matching result. */ ++ addrs += size; ++ continue; + } +- } + +- if (rc == 0) +- { +- if (h != NULL) ++ if (*pat == NULL) ++ { ++ *pat = addrfree++; ++ (*pat)->scopeid = 0; ++ } ++ uint32_t *pataddr = (*pat)->addr; ++ (*pat)->next = NULL; ++ if (added_canon || air->canon == NULL) ++ (*pat)->name = NULL; ++ else if (res.canon == NULL) + { +- /* We found data, convert it. */ +- if (!convert_hostent_to_gaih_addrtuple +- (req, AF_INET, h, &addrmem)) ++ char *canonbuf = __strdup (air->canon); ++ if (canonbuf == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } +- at = addrmem; ++ res.canon = (*pat)->name = canonbuf; + } +- else ++ ++ if (air->family[i] == AF_INET ++ && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED)) + { +- if (h_errno == NO_DATA) +- result = -EAI_NODATA; +- else +- result = -EAI_NONAME; +- goto free_and_return; ++ (*pat)->family = AF_INET6; ++ pataddr[3] = *(uint32_t *) addrs; ++ pataddr[2] = htonl (0xffff); ++ pataddr[1] = 0; ++ pataddr[0] = 0; ++ pat = &((*pat)->next); ++ added_canon = true; ++ } ++ else if (req->ai_family == AF_UNSPEC ++ || air->family[i] == req->ai_family) ++ { ++ (*pat)->family = air->family[i]; ++ memcpy (pataddr, addrs, size); ++ pat = &((*pat)->next); ++ added_canon = true; ++ if (air->family[i] == AF_INET6) ++ got_ipv6 = true; + } ++ addrs += size; + } +- else +- { +- if (h_errno == NETDB_INTERNAL) +- result = -EAI_SYSTEM; +- else if (h_errno == TRY_AGAIN) +- result = -EAI_AGAIN; +- else +- /* We made requests but they turned out no data. +- The name is known, though. */ +- result = -EAI_NODATA; + +- goto free_and_return; +- } ++ free (air); + + goto process_list; + } ++ else if (err == 0) ++ /* The database contains a negative entry. */ ++ goto free_and_return; ++ else if (__nss_not_use_nscd_hosts == 0) ++ { ++ if (h_errno == NETDB_INTERNAL && errno == ENOMEM) ++ result = -EAI_MEMORY; ++ else if (h_errno == TRY_AGAIN) ++ result = -EAI_AGAIN; ++ else ++ result = -EAI_SYSTEM; + +-#ifdef USE_NSCD +- if (__nss_not_use_nscd_hosts > 0 +- && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) +- __nss_not_use_nscd_hosts = 0; ++ goto free_and_return; ++ } ++ } ++#endif ++ ++ no_more = !__nss_database_get (nss_database_hosts, &nip); + +- if (!__nss_not_use_nscd_hosts +- && !__nss_database_custom[NSS_DBSIDX_hosts]) ++ /* If we are looking for both IPv4 and IPv6 address we don't ++ want the lookup functions to automatically promote IPv4 ++ addresses to IPv6 addresses, so we use the no_inet6 ++ function variant. */ ++ res_ctx = __resolv_context_get (); ++ if (res_ctx == NULL) ++ no_more = 1; ++ ++ while (!no_more) ++ { ++ /* Always start afresh; continue should discard previous results ++ and the hosts database does not support merge. */ ++ res.at = NULL; ++ free (res.canon); ++ free (addrmem); ++ res.canon = NULL; ++ addrmem = NULL; ++ got_ipv6 = false; ++ ++ if (do_merge) + { +- /* Try to use nscd. */ +- struct nscd_ai_result *air = NULL; +- int err = __nscd_getai (name, &air, &h_errno); +- if (air != NULL) ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ break; ++ } ++ ++ no_data = 0; ++ nss_gethostbyname4_r *fct4 = NULL; ++ ++ /* gethostbyname4_r sends out parallel A and AAAA queries and ++ is thus only suitable for PF_UNSPEC. */ ++ if (req->ai_family == PF_UNSPEC) ++ fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); ++ ++ if (fct4 != NULL) ++ { ++ while (1) + { +- /* Transform into gaih_addrtuple list. */ +- bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; +- char *addrs = air->addrs; ++ status = DL_CALL_FCT (fct4, (name, &res.at, ++ tmpbuf->data, tmpbuf->length, ++ &errno, &h_errno, ++ NULL)); ++ if (status == NSS_STATUS_SUCCESS) ++ break; ++ /* gethostbyname4_r may write into AT, so reset it. */ ++ res.at = NULL; ++ if (status != NSS_STATUS_TRYAGAIN ++ || errno != ERANGE || h_errno != NETDB_INTERNAL) ++ { ++ if (h_errno == TRY_AGAIN) ++ no_data = EAI_AGAIN; ++ else ++ no_data = h_errno == NO_DATA; ++ break; ++ } + +- addrmem = calloc (air->naddrs, sizeof (*addrmem)); +- if (addrmem == NULL) ++ if (!scratch_buffer_grow (tmpbuf)) + { ++ __resolv_context_put (res_ctx); + result = -EAI_MEMORY; + goto free_and_return; + } ++ } + +- struct gaih_addrtuple *addrfree = addrmem; +- struct gaih_addrtuple **pat = &at; ++ if (status == NSS_STATUS_SUCCESS) ++ { ++ assert (!no_data); ++ no_data = 1; + +- for (int i = 0; i < air->naddrs; ++i) ++ if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL) + { +- socklen_t size = (air->family[i] == AF_INET +- ? INADDRSZ : IN6ADDRSZ); +- +- if (!((air->family[i] == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) != 0) +- || req->ai_family == AF_UNSPEC +- || air->family[i] == req->ai_family)) ++ char *canonbuf = __strdup (res.at->name); ++ if (canonbuf == NULL) + { +- /* Skip over non-matching result. */ +- addrs += size; +- continue; ++ __resolv_context_put (res_ctx); ++ result = -EAI_MEMORY; ++ goto free_and_return; + } ++ res.canon = canonbuf; ++ } + +- if (*pat == NULL) +- { +- *pat = addrfree++; +- (*pat)->scopeid = 0; +- } +- uint32_t *pataddr = (*pat)->addr; +- (*pat)->next = NULL; +- if (added_canon || air->canon == NULL) +- (*pat)->name = NULL; +- else if (canon == NULL) +- { +- char *canonbuf = __strdup (air->canon); +- if (canonbuf == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- canon = (*pat)->name = canonbuf; +- } ++ struct gaih_addrtuple **pat = &res.at; + +- if (air->family[i] == AF_INET ++ while (*pat != NULL) ++ { ++ if ((*pat)->family == AF_INET + && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED)) ++ && (req->ai_flags & AI_V4MAPPED) != 0) + { ++ uint32_t *pataddr = (*pat)->addr; + (*pat)->family = AF_INET6; +- pataddr[3] = *(uint32_t *) addrs; ++ pataddr[3] = pataddr[0]; + pataddr[2] = htonl (0xffff); + pataddr[1] = 0; + pataddr[0] = 0; + pat = &((*pat)->next); +- added_canon = true; ++ no_data = 0; + } + else if (req->ai_family == AF_UNSPEC +- || air->family[i] == req->ai_family) ++ || (*pat)->family == req->ai_family) + { +- (*pat)->family = air->family[i]; +- memcpy (pataddr, addrs, size); + pat = &((*pat)->next); +- added_canon = true; +- if (air->family[i] == AF_INET6) ++ ++ no_data = 0; ++ if (req->ai_family == AF_INET6) + got_ipv6 = true; + } +- addrs += size; ++ else ++ *pat = ((*pat)->next); + } +- +- free (air); +- +- goto process_list; + } +- else if (err == 0) +- /* The database contains a negative entry. */ +- goto free_and_return; +- else if (__nss_not_use_nscd_hosts == 0) +- { +- if (h_errno == NETDB_INTERNAL && errno == ENOMEM) +- result = -EAI_MEMORY; +- else if (h_errno == TRY_AGAIN) +- result = -EAI_AGAIN; +- else +- result = -EAI_SYSTEM; + +- goto free_and_return; +- } ++ no_inet6_data = no_data; + } +-#endif +- +- no_more = !__nss_database_get (nss_database_hosts, &nip); +- +- /* If we are looking for both IPv4 and IPv6 address we don't +- want the lookup functions to automatically promote IPv4 +- addresses to IPv6 addresses, so we use the no_inet6 +- function variant. */ +- res_ctx = __resolv_context_get (); +- if (res_ctx == NULL) +- no_more = 1; +- +- while (!no_more) ++ else + { +- /* Always start afresh; continue should discard previous results +- and the hosts database does not support merge. */ +- at = NULL; +- free (canon); +- free (addrmem); +- canon = NULL; +- addrmem = NULL; +- got_ipv6 = false; +- +- if (do_merge) ++ nss_gethostbyname3_r *fct = NULL; ++ if (req->ai_flags & AI_CANONNAME) ++ /* No need to use this function if we do not look for ++ the canonical name. The function does not exist in ++ all NSS modules and therefore the lookup would ++ often fail. */ ++ fct = __nss_lookup_function (nip, "gethostbyname3_r"); ++ if (fct == NULL) ++ /* We are cheating here. The gethostbyname2_r ++ function does not have the same interface as ++ gethostbyname3_r but the extra arguments the ++ latter takes are added at the end. So the ++ gethostbyname2_r code will just ignore them. */ ++ fct = __nss_lookup_function (nip, "gethostbyname2_r"); ++ ++ if (fct != NULL) + { +- __set_h_errno (NETDB_INTERNAL); +- __set_errno (EBUSY); +- break; +- } +- +- no_data = 0; +- nss_gethostbyname4_r *fct4 = NULL; +- +- /* gethostbyname4_r sends out parallel A and AAAA queries and +- is thus only suitable for PF_UNSPEC. */ +- if (req->ai_family == PF_UNSPEC) +- fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); ++ struct gaih_addrtuple **pat = &res.at; + +- if (fct4 != NULL) +- { +- while (1) ++ if (req->ai_family == AF_INET6 ++ || req->ai_family == AF_UNSPEC) + { +- status = DL_CALL_FCT (fct4, (name, &at, +- tmpbuf->data, tmpbuf->length, +- &errno, &h_errno, +- NULL)); +- if (status == NSS_STATUS_SUCCESS) +- break; +- /* gethostbyname4_r may write into AT, so reset it. */ +- at = NULL; +- if (status != NSS_STATUS_TRYAGAIN +- || errno != ERANGE || h_errno != NETDB_INTERNAL) +- { +- if (h_errno == TRY_AGAIN) +- no_data = EAI_AGAIN; +- else +- no_data = h_errno == NO_DATA; +- break; +- } ++ gethosts (AF_INET6); ++ no_inet6_data = no_data; ++ inet6_status = status; ++ } ++ if (req->ai_family == AF_INET ++ || req->ai_family == AF_UNSPEC ++ || (req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) ++ /* Avoid generating the mapped addresses if we ++ know we are not going to need them. */ ++ && ((req->ai_flags & AI_ALL) || !got_ipv6))) ++ { ++ gethosts (AF_INET); + +- if (!scratch_buffer_grow (tmpbuf)) ++ if (req->ai_family == AF_INET) + { +- __resolv_context_put (res_ctx); +- result = -EAI_MEMORY; +- goto free_and_return; ++ no_inet6_data = no_data; ++ inet6_status = status; + } + } + +- if (status == NSS_STATUS_SUCCESS) ++ /* If we found one address for AF_INET or AF_INET6, ++ don't continue the search. */ ++ if (inet6_status == NSS_STATUS_SUCCESS ++ || status == NSS_STATUS_SUCCESS) + { +- assert (!no_data); +- no_data = 1; +- +- if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL) ++ if ((req->ai_flags & AI_CANONNAME) != 0 ++ && res.canon == NULL) + { +- char *canonbuf = __strdup (at->name); ++ char *canonbuf = getcanonname (nip, res.at, name); + if (canonbuf == NULL) + { + __resolv_context_put (res_ctx); + result = -EAI_MEMORY; + goto free_and_return; + } +- canon = canonbuf; +- } +- +- struct gaih_addrtuple **pat = &at; +- +- while (*pat != NULL) +- { +- if ((*pat)->family == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) != 0) +- { +- uint32_t *pataddr = (*pat)->addr; +- (*pat)->family = AF_INET6; +- pataddr[3] = pataddr[0]; +- pataddr[2] = htonl (0xffff); +- pataddr[1] = 0; +- pataddr[0] = 0; +- pat = &((*pat)->next); +- no_data = 0; +- } +- else if (req->ai_family == AF_UNSPEC +- || (*pat)->family == req->ai_family) +- { +- pat = &((*pat)->next); +- +- no_data = 0; +- if (req->ai_family == AF_INET6) +- got_ipv6 = true; +- } +- else +- *pat = ((*pat)->next); +- } +- } +- +- no_inet6_data = no_data; +- } +- else +- { +- nss_gethostbyname3_r *fct = NULL; +- if (req->ai_flags & AI_CANONNAME) +- /* No need to use this function if we do not look for +- the canonical name. The function does not exist in +- all NSS modules and therefore the lookup would +- often fail. */ +- fct = __nss_lookup_function (nip, "gethostbyname3_r"); +- if (fct == NULL) +- /* We are cheating here. The gethostbyname2_r +- function does not have the same interface as +- gethostbyname3_r but the extra arguments the +- latter takes are added at the end. So the +- gethostbyname2_r code will just ignore them. */ +- fct = __nss_lookup_function (nip, "gethostbyname2_r"); +- +- if (fct != NULL) +- { +- struct gaih_addrtuple **pat = &at; +- +- if (req->ai_family == AF_INET6 +- || req->ai_family == AF_UNSPEC) +- { +- gethosts (AF_INET6); +- no_inet6_data = no_data; +- inet6_status = status; +- } +- if (req->ai_family == AF_INET +- || req->ai_family == AF_UNSPEC +- || (req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) +- /* Avoid generating the mapped addresses if we +- know we are not going to need them. */ +- && ((req->ai_flags & AI_ALL) || !got_ipv6))) +- { +- gethosts (AF_INET); +- +- if (req->ai_family == AF_INET) +- { +- no_inet6_data = no_data; +- inet6_status = status; +- } +- } +- +- /* If we found one address for AF_INET or AF_INET6, +- don't continue the search. */ +- if (inet6_status == NSS_STATUS_SUCCESS +- || status == NSS_STATUS_SUCCESS) +- { +- if ((req->ai_flags & AI_CANONNAME) != 0 +- && canon == NULL) +- { +- char *canonbuf = getcanonname (nip, at, name); +- if (canonbuf == NULL) +- { +- __resolv_context_put (res_ctx); +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- canon = canonbuf; +- } +- status = NSS_STATUS_SUCCESS; +- } +- else +- { +- /* We can have different states for AF_INET and +- AF_INET6. Try to find a useful one for both. */ +- if (inet6_status == NSS_STATUS_TRYAGAIN) +- status = NSS_STATUS_TRYAGAIN; +- else if (status == NSS_STATUS_UNAVAIL +- && inet6_status != NSS_STATUS_UNAVAIL) +- status = inet6_status; ++ res.canon = canonbuf; + } ++ status = NSS_STATUS_SUCCESS; + } + else + { +- /* Could not locate any of the lookup functions. +- The NSS lookup code does not consistently set +- errno, so we need to supply our own error +- code here. The root cause could either be a +- resource allocation failure, or a missing +- service function in the DSO (so it should not +- be listed in /etc/nsswitch.conf). Assume the +- former, and return EBUSY. */ +- status = NSS_STATUS_UNAVAIL; +- __set_h_errno (NETDB_INTERNAL); +- __set_errno (EBUSY); ++ /* We can have different states for AF_INET and ++ AF_INET6. Try to find a useful one for both. */ ++ if (inet6_status == NSS_STATUS_TRYAGAIN) ++ status = NSS_STATUS_TRYAGAIN; ++ else if (status == NSS_STATUS_UNAVAIL ++ && inet6_status != NSS_STATUS_UNAVAIL) ++ status = inet6_status; + } + } ++ else ++ { ++ /* Could not locate any of the lookup functions. ++ The NSS lookup code does not consistently set ++ errno, so we need to supply our own error ++ code here. The root cause could either be a ++ resource allocation failure, or a missing ++ service function in the DSO (so it should not ++ be listed in /etc/nsswitch.conf). Assume the ++ former, and return EBUSY. */ ++ status = NSS_STATUS_UNAVAIL; ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ } ++ } + +- if (nss_next_action (nip, status) == NSS_ACTION_RETURN) +- break; ++ if (nss_next_action (nip, status) == NSS_ACTION_RETURN) ++ break; + +- /* The hosts database does not support MERGE. */ +- if (nss_next_action (nip, status) == NSS_ACTION_MERGE) +- do_merge = true; ++ /* The hosts database does not support MERGE. */ ++ if (nss_next_action (nip, status) == NSS_ACTION_MERGE) ++ do_merge = true; + +- nip++; +- if (nip->module == NULL) +- no_more = -1; +- } ++ nip++; ++ if (nip->module == NULL) ++ no_more = -1; ++ } + +- __resolv_context_put (res_ctx); ++ __resolv_context_put (res_ctx); + +- /* If we have a failure which sets errno, report it using +- EAI_SYSTEM. */ +- if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) +- && h_errno == NETDB_INTERNAL) +- { +- result = -EAI_SYSTEM; +- goto free_and_return; +- } ++ /* If we have a failure which sets errno, report it using ++ EAI_SYSTEM. */ ++ if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) ++ && h_errno == NETDB_INTERNAL) ++ { ++ result = -EAI_SYSTEM; ++ goto free_and_return; ++ } + +- if (no_data != 0 && no_inet6_data != 0) +- { +- /* If both requests timed out report this. */ +- if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) +- result = -EAI_AGAIN; +- else +- /* We made requests but they turned out no data. The name +- is known, though. */ +- result = -EAI_NODATA; ++ if (no_data != 0 && no_inet6_data != 0) ++ { ++ /* If both requests timed out report this. */ ++ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) ++ result = -EAI_AGAIN; ++ else ++ /* We made requests but they turned out no data. The name ++ is known, though. */ ++ result = -EAI_NODATA; + +- goto free_and_return; +- } ++ goto free_and_return; + } + + process_list: +- if (at == NULL) ++ if (res.at == NULL) + { + result = -EAI_NONAME; + goto free_and_return; +@@ -1032,21 +1044,22 @@ gaih_inet (const char *name, const struct gaih_service *service, + else + { + struct gaih_addrtuple *atr; +- atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); +- memset (at, '\0', sizeof (struct gaih_addrtuple)); ++ atr = res.at = alloca_account (sizeof (struct gaih_addrtuple), ++ alloca_used); ++ memset (res.at, '\0', sizeof (struct gaih_addrtuple)); + + if (req->ai_family == AF_UNSPEC) + { +- at->next = __alloca (sizeof (struct gaih_addrtuple)); +- memset (at->next, '\0', sizeof (struct gaih_addrtuple)); ++ res.at->next = __alloca (sizeof (struct gaih_addrtuple)); ++ memset (res.at->next, '\0', sizeof (struct gaih_addrtuple)); + } + + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) + { +- at->family = AF_INET6; ++ res.at->family = AF_INET6; + if ((req->ai_flags & AI_PASSIVE) == 0) +- memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr)); +- atr = at->next; ++ memcpy (res.at->addr, &in6addr_loopback, sizeof (struct in6_addr)); ++ atr = res.at->next; + } + + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) +@@ -1059,10 +1072,10 @@ gaih_inet (const char *name, const struct gaih_service *service, + + { + /* Set up the canonical name if we need it. */ +- if ((result = process_canonname (req, orig_name, &canon)) != 0) ++ if ((result = process_canonname (req, orig_name, &res)) != 0) + goto free_and_return; + +- struct gaih_addrtuple *at2 = at; ++ struct gaih_addrtuple *at2 = res.at; + size_t socklen; + sa_family_t family; + +@@ -1105,8 +1118,8 @@ gaih_inet (const char *name, const struct gaih_service *service, + ai->ai_addr = (void *) (ai + 1); + + /* We only add the canonical name once. */ +- ai->ai_canonname = (char *) canon; +- canon = NULL; ++ ai->ai_canonname = res.canon; ++ res.canon = NULL; + + #ifdef _HAVE_SA_LEN + ai->ai_addr->sa_len = socklen; +@@ -1152,7 +1165,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + if (malloc_name) + free ((char *) name); + free (addrmem); +- free (canon); ++ free (res.canon); + + return result; + } diff --git a/SOURCES/glibc-RHEL-2425-7.patch b/SOURCES/glibc-RHEL-2425-7.patch new file mode 100644 index 0000000..c9e0581 --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-7.patch @@ -0,0 +1,179 @@ +commit 3b5a3e5009088a029525277f36228eeb95032358 +Author: Siddhesh Poyarekar +Date: Fri Mar 4 14:57:12 2022 +0530 + + gaih_inet: Split simple gethostbyname into its own function + + Add a free_at flag in gaih_result to indicate if res.at needs to be + freed by the caller. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit b44389cb7fa28a59804571dac09cc32ebfac03d1) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 57b6834c8bb3887c..3870b2dc2edc89cd 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -120,6 +120,7 @@ struct gaih_result + { + struct gaih_addrtuple *at; + char *canon; ++ bool free_at; + }; + + /* Values for `protoflag'. */ +@@ -565,6 +566,62 @@ out: + return result; + } + ++/* If possible, call the simple, old functions, which do not support IPv6 scope ++ ids, nor retrieving the canonical name. */ ++ ++static int ++try_simple_gethostbyname (const char *name, const struct addrinfo *req, ++ struct scratch_buffer *tmpbuf, ++ struct gaih_result *res) ++{ ++ res->at = NULL; ++ ++ if (req->ai_family != AF_INET || (req->ai_flags & AI_CANONNAME) != 0) ++ return 0; ++ ++ int rc; ++ struct hostent th; ++ struct hostent *h; ++ ++ while (1) ++ { ++ rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf->data, ++ tmpbuf->length, &h, &h_errno); ++ if (rc != ERANGE || h_errno != NETDB_INTERNAL) ++ break; ++ if (!scratch_buffer_grow (tmpbuf)) ++ return -EAI_MEMORY; ++ } ++ ++ if (rc == 0) ++ { ++ if (h != NULL) ++ { ++ /* We found data, convert it. RES->AT from the conversion will ++ either be an allocated block or NULL, both of which are safe to ++ pass to free (). */ ++ if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at)) ++ return -EAI_MEMORY; ++ ++ res->free_at = true; ++ return 0; ++ } ++ if (h_errno == NO_DATA) ++ return -EAI_NODATA; ++ ++ return -EAI_NONAME; ++ } ++ ++ if (h_errno == NETDB_INTERNAL) ++ return -EAI_SYSTEM; ++ if (h_errno == TRY_AGAIN) ++ return -EAI_AGAIN; ++ ++ /* We made requests but they turned out no data. ++ The name is known, though. */ ++ return -EAI_NODATA; ++} ++ + static int + gaih_inet (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai, +@@ -610,6 +667,11 @@ gaih_inet (const char *name, const struct gaih_service *service, + else if (res.at != NULL) + goto process_list; + ++ if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++ + int no_data = 0; + int no_inet6_data = 0; + nss_action_list nip; +@@ -619,69 +681,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct resolv_context *res_ctx = NULL; + bool do_merge = false; + +- /* If we do not have to look for IPv6 addresses or the canonical +- name, use the simple, old functions, which do not support +- IPv6 scope ids, nor retrieving the canonical name. */ +- if (req->ai_family == AF_INET +- && (req->ai_flags & AI_CANONNAME) == 0) +- { +- int rc; +- struct hostent th; +- struct hostent *h; +- +- while (1) +- { +- rc = __gethostbyname2_r (name, AF_INET, &th, +- tmpbuf->data, tmpbuf->length, +- &h, &h_errno); +- if (rc != ERANGE || h_errno != NETDB_INTERNAL) +- break; +- if (!scratch_buffer_grow (tmpbuf)) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- } +- +- if (rc == 0) +- { +- if (h != NULL) +- { +- /* We found data, convert it. */ +- if (!convert_hostent_to_gaih_addrtuple +- (req, AF_INET, h, &addrmem)) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- res.at = addrmem; +- } +- else +- { +- if (h_errno == NO_DATA) +- result = -EAI_NODATA; +- else +- result = -EAI_NONAME; +- goto free_and_return; +- } +- } +- else +- { +- if (h_errno == NETDB_INTERNAL) +- result = -EAI_SYSTEM; +- else if (h_errno == TRY_AGAIN) +- result = -EAI_AGAIN; +- else +- /* We made requests but they turned out no data. +- The name is known, though. */ +- result = -EAI_NODATA; +- +- goto free_and_return; +- } +- +- goto process_list; +- } +- + #ifdef USE_NSCD + if (__nss_not_use_nscd_hosts > 0 + && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) +@@ -1165,6 +1164,8 @@ gaih_inet (const char *name, const struct gaih_service *service, + if (malloc_name) + free ((char *) name); + free (addrmem); ++ if (res.free_at) ++ free (res.at); + free (res.canon); + + return result; diff --git a/SOURCES/glibc-RHEL-2425-8.patch b/SOURCES/glibc-RHEL-2425-8.patch new file mode 100644 index 0000000..253805a --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-8.patch @@ -0,0 +1,328 @@ +commit 5914a1d55b468ccf0fb6d997a7a4e378339df735 +Author: Siddhesh Poyarekar +Date: Mon Mar 7 15:53:45 2022 +0530 + + gaih_inet: Split nscd lookup code into its own function. + + Add a new member got_ipv6 to indicate if the results have an IPv6 + result and use it instead of the local got_ipv6. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit e7e5315b7fa065a9c8bf525ca9a32f46fa4837e5) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 3870b2dc2edc89cd..7c497a88f8b5b9f8 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -121,6 +121,7 @@ struct gaih_result + struct gaih_addrtuple *at; + char *canon; + bool free_at; ++ bool got_ipv6; + }; + + /* Values for `protoflag'. */ +@@ -316,7 +317,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + res.canon = canonbuf; \ + } \ + if (_family == AF_INET6 && *pat != NULL) \ +- got_ipv6 = true; \ ++ res.got_ipv6 = true; \ + } \ + } + +@@ -467,6 +468,128 @@ get_servtuples (const struct gaih_service *service, const struct addrinfo *req, + return 0; + } + ++#ifdef USE_NSCD ++/* Query addresses from nscd cache, returning a non-zero value on error. ++ RES members have the lookup result; RES->AT is NULL if there were no errors ++ but also no results. */ ++ ++static int ++get_nscd_addresses (const char *name, const struct addrinfo *req, ++ struct gaih_result *res) ++{ ++ if (__nss_not_use_nscd_hosts > 0 ++ && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) ++ __nss_not_use_nscd_hosts = 0; ++ ++ res->at = NULL; ++ ++ if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts]) ++ return 0; ++ ++ /* Try to use nscd. */ ++ struct nscd_ai_result *air = NULL; ++ int err = __nscd_getai (name, &air, &h_errno); ++ ++ if (__glibc_unlikely (air == NULL)) ++ { ++ /* The database contains a negative entry. */ ++ if (err == 0) ++ return -EAI_NONAME; ++ if (__nss_not_use_nscd_hosts == 0) ++ { ++ if (h_errno == NETDB_INTERNAL && errno == ENOMEM) ++ return -EAI_MEMORY; ++ if (h_errno == TRY_AGAIN) ++ return -EAI_AGAIN; ++ return -EAI_SYSTEM; ++ } ++ return 0; ++ } ++ ++ /* Transform into gaih_addrtuple list. */ ++ int result = 0; ++ char *addrs = air->addrs; ++ ++ struct gaih_addrtuple *addrfree = calloc (air->naddrs, sizeof (*addrfree)); ++ struct gaih_addrtuple *at = calloc (air->naddrs, sizeof (*at)); ++ if (at == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ ++ res->free_at = true; ++ ++ int count = 0; ++ for (int i = 0; i < air->naddrs; ++i) ++ { ++ socklen_t size = (air->family[i] == AF_INET ++ ? INADDRSZ : IN6ADDRSZ); ++ ++ if (!((air->family[i] == AF_INET ++ && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) != 0) ++ || req->ai_family == AF_UNSPEC ++ || air->family[i] == req->ai_family)) ++ { ++ /* Skip over non-matching result. */ ++ addrs += size; ++ continue; ++ } ++ ++ if (air->family[i] == AF_INET && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED)) ++ { ++ at[count].family = AF_INET6; ++ at[count].addr[3] = *(uint32_t *) addrs; ++ at[count].addr[2] = htonl (0xffff); ++ } ++ else if (req->ai_family == AF_UNSPEC ++ || air->family[count] == req->ai_family) ++ { ++ at[count].family = air->family[count]; ++ memcpy (at[count].addr, addrs, size); ++ if (air->family[count] == AF_INET6) ++ res->got_ipv6 = true; ++ } ++ at[count].next = at + count + 1; ++ count++; ++ addrs += size; ++ } ++ ++ if ((req->ai_flags & AI_CANONNAME) && air->canon != NULL) ++ { ++ char *canonbuf = __strdup (air->canon); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ ++ if (count == 0) ++ { ++ result = -EAI_NONAME; ++ goto out; ++ } ++ ++ at[count - 1].next = NULL; ++ ++ res->at = at; ++ ++out: ++ free (air); ++ if (result != 0) ++ { ++ free (at); ++ res->free_at = false; ++ } ++ ++ return result; ++} ++#endif ++ + /* Convert numeric addresses to binary into RES. On failure, RES->AT is set to + NULL and an error code is returned. If AI_NUMERIC_HOST is not requested and + the function cannot determine a result, RES->AT is set to NULL and 0 +@@ -630,7 +753,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct gaih_servtuple st[sizeof (gaih_inet_typeproto) + / sizeof (struct gaih_typeproto)] = {0}; + +- bool got_ipv6 = false; + const char *orig_name = name; + + /* Reserve stack memory for the scratch buffer in the getaddrinfo +@@ -672,6 +794,13 @@ gaih_inet (const char *name, const struct gaih_service *service, + else if (res.at != NULL) + goto process_list; + ++#ifdef USE_NSCD ++ if ((result = get_nscd_addresses (name, req, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++#endif ++ + int no_data = 0; + int no_inet6_data = 0; + nss_action_list nip; +@@ -681,115 +810,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + struct resolv_context *res_ctx = NULL; + bool do_merge = false; + +-#ifdef USE_NSCD +- if (__nss_not_use_nscd_hosts > 0 +- && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) +- __nss_not_use_nscd_hosts = 0; +- +- if (!__nss_not_use_nscd_hosts +- && !__nss_database_custom[NSS_DBSIDX_hosts]) +- { +- /* Try to use nscd. */ +- struct nscd_ai_result *air = NULL; +- int err = __nscd_getai (name, &air, &h_errno); +- if (air != NULL) +- { +- /* Transform into gaih_addrtuple list. */ +- bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; +- char *addrs = air->addrs; +- +- addrmem = calloc (air->naddrs, sizeof (*addrmem)); +- if (addrmem == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- +- struct gaih_addrtuple *addrfree = addrmem; +- struct gaih_addrtuple **pat = &res.at; +- +- for (int i = 0; i < air->naddrs; ++i) +- { +- socklen_t size = (air->family[i] == AF_INET +- ? INADDRSZ : IN6ADDRSZ); +- +- if (!((air->family[i] == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) != 0) +- || req->ai_family == AF_UNSPEC +- || air->family[i] == req->ai_family)) +- { +- /* Skip over non-matching result. */ +- addrs += size; +- continue; +- } +- +- if (*pat == NULL) +- { +- *pat = addrfree++; +- (*pat)->scopeid = 0; +- } +- uint32_t *pataddr = (*pat)->addr; +- (*pat)->next = NULL; +- if (added_canon || air->canon == NULL) +- (*pat)->name = NULL; +- else if (res.canon == NULL) +- { +- char *canonbuf = __strdup (air->canon); +- if (canonbuf == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- res.canon = (*pat)->name = canonbuf; +- } +- +- if (air->family[i] == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED)) +- { +- (*pat)->family = AF_INET6; +- pataddr[3] = *(uint32_t *) addrs; +- pataddr[2] = htonl (0xffff); +- pataddr[1] = 0; +- pataddr[0] = 0; +- pat = &((*pat)->next); +- added_canon = true; +- } +- else if (req->ai_family == AF_UNSPEC +- || air->family[i] == req->ai_family) +- { +- (*pat)->family = air->family[i]; +- memcpy (pataddr, addrs, size); +- pat = &((*pat)->next); +- added_canon = true; +- if (air->family[i] == AF_INET6) +- got_ipv6 = true; +- } +- addrs += size; +- } +- +- free (air); +- +- goto process_list; +- } +- else if (err == 0) +- /* The database contains a negative entry. */ +- goto free_and_return; +- else if (__nss_not_use_nscd_hosts == 0) +- { +- if (h_errno == NETDB_INTERNAL && errno == ENOMEM) +- result = -EAI_MEMORY; +- else if (h_errno == TRY_AGAIN) +- result = -EAI_AGAIN; +- else +- result = -EAI_SYSTEM; +- +- goto free_and_return; +- } +- } +-#endif +- + no_more = !__nss_database_get (nss_database_hosts, &nip); + + /* If we are looking for both IPv4 and IPv6 address we don't +@@ -897,7 +917,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + + no_data = 0; + if (req->ai_family == AF_INET6) +- got_ipv6 = true; ++ res.got_ipv6 = true; + } + else + *pat = ((*pat)->next); +@@ -940,7 +960,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + && (req->ai_flags & AI_V4MAPPED) + /* Avoid generating the mapped addresses if we + know we are not going to need them. */ +- && ((req->ai_flags & AI_ALL) || !got_ipv6))) ++ && ((req->ai_flags & AI_ALL) || !res.got_ipv6))) + { + gethosts (AF_INET); + +@@ -1091,7 +1111,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + /* If we looked up IPv4 mapped address discard them here if + the caller isn't interested in all address and we have + found at least one IPv6 address. */ +- if (got_ipv6 ++ if (res.got_ipv6 + && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED + && IN6_IS_ADDR_V4MAPPED (at2->addr)) + goto ignore; diff --git a/SOURCES/glibc-RHEL-2425-9.patch b/SOURCES/glibc-RHEL-2425-9.patch new file mode 100644 index 0000000..bc3766b --- /dev/null +++ b/SOURCES/glibc-RHEL-2425-9.patch @@ -0,0 +1,673 @@ +commit ec71cb961121760f81e55af5489e658dc89e96e6 +Author: Siddhesh Poyarekar +Date: Mon Mar 7 15:56:22 2022 +0530 + + gaih_inet: separate nss lookup loop into its own function + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + (cherry picked from commit 906cecbe0889e601c91d9aba738049c73ebe4dd2) + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 7c497a88f8b5b9f8..145ea6fa381ad14b 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -159,6 +159,14 @@ static const struct addrinfo default_hints = + .ai_next = NULL + }; + ++static void ++gaih_result_reset (struct gaih_result *res) ++{ ++ if (res->free_at) ++ free (res->at); ++ free (res->canon); ++ memset (res, 0, sizeof (*res)); ++} + + static int + gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, +@@ -197,13 +205,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, + + /* Convert struct hostent to a list of struct gaih_addrtuple objects. h_name + is not copied, and the struct hostent object must not be deallocated +- prematurely. The new addresses are appended to the tuple array in +- RESULT. */ ++ prematurely. The new addresses are appended to the tuple array in RES. */ + static bool +-convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, +- int family, +- struct hostent *h, +- struct gaih_addrtuple **result) ++convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, ++ struct hostent *h, struct gaih_result *res) + { + /* Count the number of addresses in h->h_addr_list. */ + size_t count = 0; +@@ -215,7 +220,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr)) + return true; + +- struct gaih_addrtuple *array = *result; ++ struct gaih_addrtuple *array = res->at; + size_t old = 0; + + while (array != NULL) +@@ -224,12 +229,14 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + array = array->next; + } + +- array = realloc (*result, (old + count) * sizeof (*array)); ++ array = realloc (res->at, (old + count) * sizeof (*array)); + + if (array == NULL) + return false; + +- *result = array; ++ res->got_ipv6 = family == AF_INET6; ++ res->at = array; ++ res->free_at = true; + + /* Update the next pointers on reallocation. */ + for (size_t i = 0; i < old; i++) +@@ -278,7 +285,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + { \ + __resolv_context_put (res_ctx); \ + result = -EAI_MEMORY; \ +- goto free_and_return; \ ++ goto out; \ + } \ + } \ + if (status == NSS_STATUS_NOTFOUND \ +@@ -288,7 +295,7 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + { \ + __resolv_context_put (res_ctx); \ + result = -EAI_SYSTEM; \ +- goto free_and_return; \ ++ goto out; \ + } \ + if (h_errno == TRY_AGAIN) \ + no_data = EAI_AGAIN; \ +@@ -297,27 +304,24 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, + } \ + else if (status == NSS_STATUS_SUCCESS) \ + { \ +- if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem)) \ ++ if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, res)) \ + { \ + __resolv_context_put (res_ctx); \ + result = -EAI_SYSTEM; \ +- goto free_and_return; \ ++ goto out; \ + } \ +- *pat = addrmem; \ + \ +- if (localcanon != NULL && res.canon == NULL) \ ++ if (localcanon != NULL && res->canon == NULL) \ + { \ + char *canonbuf = __strdup (localcanon); \ + if (canonbuf == NULL) \ + { \ + __resolv_context_put (res_ctx); \ + result = -EAI_SYSTEM; \ +- goto free_and_return; \ ++ goto out; \ + } \ +- res.canon = canonbuf; \ ++ res->canon = canonbuf; \ + } \ +- if (_family == AF_INET6 && *pat != NULL) \ +- res.got_ipv6 = true; \ + } \ + } + +@@ -590,6 +594,260 @@ out: + } + #endif + ++static int ++get_nss_addresses (const char *name, const struct addrinfo *req, ++ struct scratch_buffer *tmpbuf, struct gaih_result *res) ++{ ++ int no_data = 0; ++ int no_inet6_data = 0; ++ nss_action_list nip; ++ enum nss_status inet6_status = NSS_STATUS_UNAVAIL; ++ enum nss_status status = NSS_STATUS_UNAVAIL; ++ int no_more; ++ struct resolv_context *res_ctx = NULL; ++ bool do_merge = false; ++ int result = 0; ++ ++ no_more = !__nss_database_get (nss_database_hosts, &nip); ++ ++ /* If we are looking for both IPv4 and IPv6 address we don't ++ want the lookup functions to automatically promote IPv4 ++ addresses to IPv6 addresses, so we use the no_inet6 ++ function variant. */ ++ res_ctx = __resolv_context_get (); ++ if (res_ctx == NULL) ++ no_more = 1; ++ ++ while (!no_more) ++ { ++ /* Always start afresh; continue should discard previous results ++ and the hosts database does not support merge. */ ++ gaih_result_reset (res); ++ ++ if (do_merge) ++ { ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ break; ++ } ++ ++ no_data = 0; ++ nss_gethostbyname4_r *fct4 = NULL; ++ ++ /* gethostbyname4_r sends out parallel A and AAAA queries and ++ is thus only suitable for PF_UNSPEC. */ ++ if (req->ai_family == PF_UNSPEC) ++ fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); ++ ++ if (fct4 != NULL) ++ { ++ while (1) ++ { ++ status = DL_CALL_FCT (fct4, (name, &res->at, ++ tmpbuf->data, tmpbuf->length, ++ &errno, &h_errno, ++ NULL)); ++ if (status == NSS_STATUS_SUCCESS) ++ break; ++ /* gethostbyname4_r may write into AT, so reset it. */ ++ res->at = NULL; ++ if (status != NSS_STATUS_TRYAGAIN ++ || errno != ERANGE || h_errno != NETDB_INTERNAL) ++ { ++ if (h_errno == TRY_AGAIN) ++ no_data = EAI_AGAIN; ++ else ++ no_data = h_errno == NO_DATA; ++ break; ++ } ++ ++ if (!scratch_buffer_grow (tmpbuf)) ++ { ++ __resolv_context_put (res_ctx); ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ } ++ ++ if (status == NSS_STATUS_SUCCESS) ++ { ++ assert (!no_data); ++ no_data = 1; ++ ++ if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL) ++ { ++ char *canonbuf = __strdup (res->at->name); ++ if (canonbuf == NULL) ++ { ++ __resolv_context_put (res_ctx); ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ ++ struct gaih_addrtuple **pat = &res->at; ++ ++ while (*pat != NULL) ++ { ++ if ((*pat)->family == AF_INET ++ && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) != 0) ++ { ++ uint32_t *pataddr = (*pat)->addr; ++ (*pat)->family = AF_INET6; ++ pataddr[3] = pataddr[0]; ++ pataddr[2] = htonl (0xffff); ++ pataddr[1] = 0; ++ pataddr[0] = 0; ++ pat = &((*pat)->next); ++ no_data = 0; ++ } ++ else if (req->ai_family == AF_UNSPEC ++ || (*pat)->family == req->ai_family) ++ { ++ pat = &((*pat)->next); ++ ++ no_data = 0; ++ if (req->ai_family == AF_INET6) ++ res->got_ipv6 = true; ++ } ++ else ++ *pat = ((*pat)->next); ++ } ++ } ++ ++ no_inet6_data = no_data; ++ } ++ else ++ { ++ nss_gethostbyname3_r *fct = NULL; ++ if (req->ai_flags & AI_CANONNAME) ++ /* No need to use this function if we do not look for ++ the canonical name. The function does not exist in ++ all NSS modules and therefore the lookup would ++ often fail. */ ++ fct = __nss_lookup_function (nip, "gethostbyname3_r"); ++ if (fct == NULL) ++ /* We are cheating here. The gethostbyname2_r ++ function does not have the same interface as ++ gethostbyname3_r but the extra arguments the ++ latter takes are added at the end. So the ++ gethostbyname2_r code will just ignore them. */ ++ fct = __nss_lookup_function (nip, "gethostbyname2_r"); ++ ++ if (fct != NULL) ++ { ++ if (req->ai_family == AF_INET6 ++ || req->ai_family == AF_UNSPEC) ++ { ++ gethosts (AF_INET6); ++ no_inet6_data = no_data; ++ inet6_status = status; ++ } ++ if (req->ai_family == AF_INET ++ || req->ai_family == AF_UNSPEC ++ || (req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) ++ /* Avoid generating the mapped addresses if we ++ know we are not going to need them. */ ++ && ((req->ai_flags & AI_ALL) || !res->got_ipv6))) ++ { ++ gethosts (AF_INET); ++ ++ if (req->ai_family == AF_INET) ++ { ++ no_inet6_data = no_data; ++ inet6_status = status; ++ } ++ } ++ ++ /* If we found one address for AF_INET or AF_INET6, ++ don't continue the search. */ ++ if (inet6_status == NSS_STATUS_SUCCESS ++ || status == NSS_STATUS_SUCCESS) ++ { ++ if ((req->ai_flags & AI_CANONNAME) != 0 ++ && res->canon == NULL) ++ { ++ char *canonbuf = getcanonname (nip, res->at, name); ++ if (canonbuf == NULL) ++ { ++ __resolv_context_put (res_ctx); ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ status = NSS_STATUS_SUCCESS; ++ } ++ else ++ { ++ /* We can have different states for AF_INET and ++ AF_INET6. Try to find a useful one for both. */ ++ if (inet6_status == NSS_STATUS_TRYAGAIN) ++ status = NSS_STATUS_TRYAGAIN; ++ else if (status == NSS_STATUS_UNAVAIL ++ && inet6_status != NSS_STATUS_UNAVAIL) ++ status = inet6_status; ++ } ++ } ++ else ++ { ++ /* Could not locate any of the lookup functions. ++ The NSS lookup code does not consistently set ++ errno, so we need to supply our own error ++ code here. The root cause could either be a ++ resource allocation failure, or a missing ++ service function in the DSO (so it should not ++ be listed in /etc/nsswitch.conf). Assume the ++ former, and return EBUSY. */ ++ status = NSS_STATUS_UNAVAIL; ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ } ++ } ++ ++ if (nss_next_action (nip, status) == NSS_ACTION_RETURN) ++ break; ++ ++ /* The hosts database does not support MERGE. */ ++ if (nss_next_action (nip, status) == NSS_ACTION_MERGE) ++ do_merge = true; ++ ++ nip++; ++ if (nip->module == NULL) ++ no_more = -1; ++ } ++ ++ __resolv_context_put (res_ctx); ++ ++ /* If we have a failure which sets errno, report it using ++ EAI_SYSTEM. */ ++ if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) ++ && h_errno == NETDB_INTERNAL) ++ { ++ result = -EAI_SYSTEM; ++ goto out; ++ } ++ ++ if (no_data != 0 && no_inet6_data != 0) ++ { ++ /* If both requests timed out report this. */ ++ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) ++ result = -EAI_AGAIN; ++ else ++ /* We made requests but they turned out no data. The name ++ is known, though. */ ++ result = -EAI_NODATA; ++ } ++ ++out: ++ if (result != 0) ++ gaih_result_reset (res); ++ return result; ++} ++ + /* Convert numeric addresses to binary into RES. On failure, RES->AT is set to + NULL and an error code is returned. If AI_NUMERIC_HOST is not requested and + the function cannot determine a result, RES->AT is set to NULL and 0 +@@ -723,7 +981,7 @@ try_simple_gethostbyname (const char *name, const struct addrinfo *req, + /* We found data, convert it. RES->AT from the conversion will + either be an allocated block or NULL, both of which are safe to + pass to free (). */ +- if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, &res->at)) ++ if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res)) + return -EAI_MEMORY; + + res->free_at = true; +@@ -801,264 +1059,14 @@ gaih_inet (const char *name, const struct gaih_service *service, + goto process_list; + #endif + +- int no_data = 0; +- int no_inet6_data = 0; +- nss_action_list nip; +- enum nss_status inet6_status = NSS_STATUS_UNAVAIL; +- enum nss_status status = NSS_STATUS_UNAVAIL; +- int no_more; +- struct resolv_context *res_ctx = NULL; +- bool do_merge = false; +- +- no_more = !__nss_database_get (nss_database_hosts, &nip); +- +- /* If we are looking for both IPv4 and IPv6 address we don't +- want the lookup functions to automatically promote IPv4 +- addresses to IPv6 addresses, so we use the no_inet6 +- function variant. */ +- res_ctx = __resolv_context_get (); +- if (res_ctx == NULL) +- no_more = 1; +- +- while (!no_more) +- { +- /* Always start afresh; continue should discard previous results +- and the hosts database does not support merge. */ +- res.at = NULL; +- free (res.canon); +- free (addrmem); +- res.canon = NULL; +- addrmem = NULL; +- got_ipv6 = false; +- +- if (do_merge) +- { +- __set_h_errno (NETDB_INTERNAL); +- __set_errno (EBUSY); +- break; +- } +- +- no_data = 0; +- nss_gethostbyname4_r *fct4 = NULL; +- +- /* gethostbyname4_r sends out parallel A and AAAA queries and +- is thus only suitable for PF_UNSPEC. */ +- if (req->ai_family == PF_UNSPEC) +- fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); +- +- if (fct4 != NULL) +- { +- while (1) +- { +- status = DL_CALL_FCT (fct4, (name, &res.at, +- tmpbuf->data, tmpbuf->length, +- &errno, &h_errno, +- NULL)); +- if (status == NSS_STATUS_SUCCESS) +- break; +- /* gethostbyname4_r may write into AT, so reset it. */ +- res.at = NULL; +- if (status != NSS_STATUS_TRYAGAIN +- || errno != ERANGE || h_errno != NETDB_INTERNAL) +- { +- if (h_errno == TRY_AGAIN) +- no_data = EAI_AGAIN; +- else +- no_data = h_errno == NO_DATA; +- break; +- } +- +- if (!scratch_buffer_grow (tmpbuf)) +- { +- __resolv_context_put (res_ctx); +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- } +- +- if (status == NSS_STATUS_SUCCESS) +- { +- assert (!no_data); +- no_data = 1; +- +- if ((req->ai_flags & AI_CANONNAME) != 0 && res.canon == NULL) +- { +- char *canonbuf = __strdup (res.at->name); +- if (canonbuf == NULL) +- { +- __resolv_context_put (res_ctx); +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- res.canon = canonbuf; +- } +- +- struct gaih_addrtuple **pat = &res.at; +- +- while (*pat != NULL) +- { +- if ((*pat)->family == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) != 0) +- { +- uint32_t *pataddr = (*pat)->addr; +- (*pat)->family = AF_INET6; +- pataddr[3] = pataddr[0]; +- pataddr[2] = htonl (0xffff); +- pataddr[1] = 0; +- pataddr[0] = 0; +- pat = &((*pat)->next); +- no_data = 0; +- } +- else if (req->ai_family == AF_UNSPEC +- || (*pat)->family == req->ai_family) +- { +- pat = &((*pat)->next); +- +- no_data = 0; +- if (req->ai_family == AF_INET6) +- res.got_ipv6 = true; +- } +- else +- *pat = ((*pat)->next); +- } +- } +- +- no_inet6_data = no_data; +- } +- else +- { +- nss_gethostbyname3_r *fct = NULL; +- if (req->ai_flags & AI_CANONNAME) +- /* No need to use this function if we do not look for +- the canonical name. The function does not exist in +- all NSS modules and therefore the lookup would +- often fail. */ +- fct = __nss_lookup_function (nip, "gethostbyname3_r"); +- if (fct == NULL) +- /* We are cheating here. The gethostbyname2_r +- function does not have the same interface as +- gethostbyname3_r but the extra arguments the +- latter takes are added at the end. So the +- gethostbyname2_r code will just ignore them. */ +- fct = __nss_lookup_function (nip, "gethostbyname2_r"); +- +- if (fct != NULL) +- { +- struct gaih_addrtuple **pat = &res.at; +- +- if (req->ai_family == AF_INET6 +- || req->ai_family == AF_UNSPEC) +- { +- gethosts (AF_INET6); +- no_inet6_data = no_data; +- inet6_status = status; +- } +- if (req->ai_family == AF_INET +- || req->ai_family == AF_UNSPEC +- || (req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) +- /* Avoid generating the mapped addresses if we +- know we are not going to need them. */ +- && ((req->ai_flags & AI_ALL) || !res.got_ipv6))) +- { +- gethosts (AF_INET); +- +- if (req->ai_family == AF_INET) +- { +- no_inet6_data = no_data; +- inet6_status = status; +- } +- } +- +- /* If we found one address for AF_INET or AF_INET6, +- don't continue the search. */ +- if (inet6_status == NSS_STATUS_SUCCESS +- || status == NSS_STATUS_SUCCESS) +- { +- if ((req->ai_flags & AI_CANONNAME) != 0 +- && res.canon == NULL) +- { +- char *canonbuf = getcanonname (nip, res.at, name); +- if (canonbuf == NULL) +- { +- __resolv_context_put (res_ctx); +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- res.canon = canonbuf; +- } +- status = NSS_STATUS_SUCCESS; +- } +- else +- { +- /* We can have different states for AF_INET and +- AF_INET6. Try to find a useful one for both. */ +- if (inet6_status == NSS_STATUS_TRYAGAIN) +- status = NSS_STATUS_TRYAGAIN; +- else if (status == NSS_STATUS_UNAVAIL +- && inet6_status != NSS_STATUS_UNAVAIL) +- status = inet6_status; +- } +- } +- else +- { +- /* Could not locate any of the lookup functions. +- The NSS lookup code does not consistently set +- errno, so we need to supply our own error +- code here. The root cause could either be a +- resource allocation failure, or a missing +- service function in the DSO (so it should not +- be listed in /etc/nsswitch.conf). Assume the +- former, and return EBUSY. */ +- status = NSS_STATUS_UNAVAIL; +- __set_h_errno (NETDB_INTERNAL); +- __set_errno (EBUSY); +- } +- } +- +- if (nss_next_action (nip, status) == NSS_ACTION_RETURN) +- break; +- +- /* The hosts database does not support MERGE. */ +- if (nss_next_action (nip, status) == NSS_ACTION_MERGE) +- do_merge = true; +- +- nip++; +- if (nip->module == NULL) +- no_more = -1; +- } +- +- __resolv_context_put (res_ctx); +- +- /* If we have a failure which sets errno, report it using +- EAI_SYSTEM. */ +- if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) +- && h_errno == NETDB_INTERNAL) +- { +- result = -EAI_SYSTEM; +- goto free_and_return; +- } +- +- if (no_data != 0 && no_inet6_data != 0) +- { +- /* If both requests timed out report this. */ +- if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) +- result = -EAI_AGAIN; +- else +- /* We made requests but they turned out no data. The name +- is known, though. */ +- result = -EAI_NODATA; +- +- goto free_and_return; +- } ++ if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; + +- process_list: +- if (res.at == NULL) +- { +- result = -EAI_NONAME; +- goto free_and_return; +- } ++ /* None of the lookups worked, so name not found. */ ++ result = -EAI_NONAME; ++ goto free_and_return; + } + else + { +@@ -1089,6 +1097,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + } + ++process_list: + { + /* Set up the canonical name if we need it. */ + if ((result = process_canonname (req, orig_name, &res)) != 0) diff --git a/SOURCES/glibc-RHEL-2437.patch b/SOURCES/glibc-RHEL-2437.patch new file mode 100644 index 0000000..a818861 --- /dev/null +++ b/SOURCES/glibc-RHEL-2437.patch @@ -0,0 +1,973 @@ +commit 1c37b8022e8763fedbb3f79c02e05c6acfe5a215 +Author: Siddhesh Poyarekar +Date: Thu Mar 17 11:44:34 2022 +0530 + + Simplify allocations and fix merge and continue actions [BZ #28931] + + Allocations for address tuples is currently a bit confusing because of + the pointer chasing through PAT, making it hard to observe the sequence + in which allocations have been made. Narrow scope of the pointer + chasing through PAT so that it is only used where necessary. + + This also tightens actions behaviour with the hosts database in + getaddrinfo to comply with the manual text. The "continue" action + discards previous results and the "merge" action results in an immedate + lookup failure. Consequently, chaining of allocations across modules is + no longer necessary, thus opening up cleanup opportunities. + + A test has been added that checks some combinations to ensure that they + work correctly. + + Resolves: BZ #28931 + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: DJ Delorie + +Conflicts: + nss/Makefile + (Missing tests) + +diff --git a/nss/Makefile b/nss/Makefile +index 716bc8f6ef5276b0..beb30ac2667fe998 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -70,6 +70,7 @@ tests-container = \ + tst-nss-files-hosts-long \ + tst-nss-db-endpwent \ + tst-nss-db-endgrent \ ++ tst-nss-gai-actions \ + tst-reload1 tst-reload2 + + # Tests which need libdl +diff --git a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c +new file mode 100644 +index 0000000000000000..efca6cd1837a172a +--- /dev/null ++++ b/nss/tst-nss-gai-actions.c +@@ -0,0 +1,149 @@ ++/* Test continue and merge NSS actions for getaddrinfo. ++ Copyright The GNU Toolchain Authors. ++ 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 ++ ++enum ++{ ++ ACTION_MERGE = 0, ++ ACTION_CONTINUE, ++}; ++ ++static const char * ++family_str (int family) ++{ ++ switch (family) ++ { ++ case AF_UNSPEC: ++ return "AF_UNSPEC"; ++ case AF_INET: ++ return "AF_INET"; ++ default: ++ __builtin_unreachable (); ++ } ++} ++ ++static const char * ++action_str (int action) ++{ ++ switch (action) ++ { ++ case ACTION_MERGE: ++ return "merge"; ++ case ACTION_CONTINUE: ++ return "continue"; ++ default: ++ __builtin_unreachable (); ++ } ++} ++ ++static void ++do_one_test (int action, int family, bool canon) ++{ ++ struct addrinfo hints = ++ { ++ .ai_family = family, ++ }; ++ ++ struct addrinfo *ai; ++ ++ if (canon) ++ hints.ai_flags = AI_CANONNAME; ++ ++ printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n", ++ action_str (action), family_str (family), ++ canon ? "AI_CANONNAME" : ""); ++ ++ int ret = getaddrinfo ("example.org", "80", &hints, &ai); ++ ++ switch (action) ++ { ++ case ACTION_MERGE: ++ if (ret == 0) ++ { ++ char *formatted = support_format_addrinfo (ai, ret); ++ ++ printf ("merge unexpectedly succeeded:\n %s\n", formatted); ++ support_record_failure (); ++ free (formatted); ++ } ++ else ++ return; ++ case ACTION_CONTINUE: ++ { ++ char *formatted = support_format_addrinfo (ai, ret); ++ ++ /* Verify that the result appears exactly once. */ ++ const char *expected = "address: STREAM/TCP 192.0.0.1 80\n" ++ "address: DGRAM/UDP 192.0.0.1 80\n" ++ "address: RAW/IP 192.0.0.1 80\n"; ++ ++ const char *contains = strstr (formatted, expected); ++ const char *contains2 = NULL; ++ ++ if (contains != NULL) ++ contains2 = strstr (contains + strlen (expected), expected); ++ ++ if (contains == NULL || contains2 != NULL) ++ { ++ printf ("continue failed:\n%s\n", formatted); ++ support_record_failure (); ++ } ++ ++ free (formatted); ++ break; ++ } ++ default: ++ __builtin_unreachable (); ++ } ++} ++ ++static void ++do_one_test_set (int action) ++{ ++ char buf[32]; ++ ++ snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files", ++ action_str (action)); ++ __nss_configure_lookup ("hosts", buf); ++ ++ do_one_test (action, AF_UNSPEC, false); ++ do_one_test (action, AF_INET, false); ++ do_one_test (action, AF_INET, true); ++} ++ ++static int ++do_test (void) ++{ ++ do_one_test_set (ACTION_CONTINUE); ++ do_one_test_set (ACTION_MERGE); ++ return 0; ++} ++ ++#include +diff --git a/nss/tst-nss-gai-actions.root/etc/host.conf b/nss/tst-nss-gai-actions.root/etc/host.conf +new file mode 100644 +index 0000000000000000..d1a59f73a90f2993 +--- /dev/null ++++ b/nss/tst-nss-gai-actions.root/etc/host.conf +@@ -0,0 +1 @@ ++multi on +diff --git a/nss/tst-nss-gai-actions.root/etc/hosts b/nss/tst-nss-gai-actions.root/etc/hosts +new file mode 100644 +index 0000000000000000..50ce9774dc2c21d9 +--- /dev/null ++++ b/nss/tst-nss-gai-actions.root/etc/hosts +@@ -0,0 +1,508 @@ ++192.0.0.1 example.org ++192.0.0.2 example.org ++192.0.0.3 example.org ++192.0.0.4 example.org ++192.0.0.5 example.org ++192.0.0.6 example.org ++192.0.0.7 example.org ++192.0.0.8 example.org ++192.0.0.9 example.org ++192.0.0.10 example.org ++192.0.0.11 example.org ++192.0.0.12 example.org ++192.0.0.13 example.org ++192.0.0.14 example.org ++192.0.0.15 example.org ++192.0.0.16 example.org ++192.0.0.17 example.org ++192.0.0.18 example.org ++192.0.0.19 example.org ++192.0.0.20 example.org ++192.0.0.21 example.org ++192.0.0.22 example.org ++192.0.0.23 example.org ++192.0.0.24 example.org ++192.0.0.25 example.org ++192.0.0.26 example.org ++192.0.0.27 example.org ++192.0.0.28 example.org ++192.0.0.29 example.org ++192.0.0.30 example.org ++192.0.0.31 example.org ++192.0.0.32 example.org ++192.0.0.33 example.org ++192.0.0.34 example.org ++192.0.0.35 example.org ++192.0.0.36 example.org ++192.0.0.37 example.org ++192.0.0.38 example.org ++192.0.0.39 example.org ++192.0.0.40 example.org ++192.0.0.41 example.org ++192.0.0.42 example.org ++192.0.0.43 example.org ++192.0.0.44 example.org ++192.0.0.45 example.org ++192.0.0.46 example.org ++192.0.0.47 example.org ++192.0.0.48 example.org ++192.0.0.49 example.org ++192.0.0.50 example.org ++192.0.0.51 example.org ++192.0.0.52 example.org ++192.0.0.53 example.org ++192.0.0.54 example.org ++192.0.0.55 example.org ++192.0.0.56 example.org ++192.0.0.57 example.org ++192.0.0.58 example.org ++192.0.0.59 example.org ++192.0.0.60 example.org ++192.0.0.61 example.org ++192.0.0.62 example.org ++192.0.0.63 example.org ++192.0.0.64 example.org ++192.0.0.65 example.org ++192.0.0.66 example.org ++192.0.0.67 example.org ++192.0.0.68 example.org ++192.0.0.69 example.org ++192.0.0.70 example.org ++192.0.0.71 example.org ++192.0.0.72 example.org ++192.0.0.73 example.org ++192.0.0.74 example.org ++192.0.0.75 example.org ++192.0.0.76 example.org ++192.0.0.77 example.org ++192.0.0.78 example.org ++192.0.0.79 example.org ++192.0.0.80 example.org ++192.0.0.81 example.org ++192.0.0.82 example.org ++192.0.0.83 example.org ++192.0.0.84 example.org ++192.0.0.85 example.org ++192.0.0.86 example.org ++192.0.0.87 example.org ++192.0.0.88 example.org ++192.0.0.89 example.org ++192.0.0.90 example.org ++192.0.0.91 example.org ++192.0.0.92 example.org ++192.0.0.93 example.org ++192.0.0.94 example.org ++192.0.0.95 example.org ++192.0.0.96 example.org ++192.0.0.97 example.org ++192.0.0.98 example.org ++192.0.0.99 example.org ++192.0.0.100 example.org ++192.0.0.101 example.org ++192.0.0.102 example.org ++192.0.0.103 example.org ++192.0.0.104 example.org ++192.0.0.105 example.org ++192.0.0.106 example.org ++192.0.0.107 example.org ++192.0.0.108 example.org ++192.0.0.109 example.org ++192.0.0.110 example.org ++192.0.0.111 example.org ++192.0.0.112 example.org ++192.0.0.113 example.org ++192.0.0.114 example.org ++192.0.0.115 example.org ++192.0.0.116 example.org ++192.0.0.117 example.org ++192.0.0.118 example.org ++192.0.0.119 example.org ++192.0.0.120 example.org ++192.0.0.121 example.org ++192.0.0.122 example.org ++192.0.0.123 example.org ++192.0.0.124 example.org ++192.0.0.125 example.org ++192.0.0.126 example.org ++192.0.0.127 example.org ++192.0.0.128 example.org ++192.0.0.129 example.org ++192.0.0.130 example.org ++192.0.0.131 example.org ++192.0.0.132 example.org ++192.0.0.133 example.org ++192.0.0.134 example.org ++192.0.0.135 example.org ++192.0.0.136 example.org ++192.0.0.137 example.org ++192.0.0.138 example.org ++192.0.0.139 example.org ++192.0.0.140 example.org ++192.0.0.141 example.org ++192.0.0.142 example.org ++192.0.0.143 example.org ++192.0.0.144 example.org ++192.0.0.145 example.org ++192.0.0.146 example.org ++192.0.0.147 example.org ++192.0.0.148 example.org ++192.0.0.149 example.org ++192.0.0.150 example.org ++192.0.0.151 example.org ++192.0.0.152 example.org ++192.0.0.153 example.org ++192.0.0.154 example.org ++192.0.0.155 example.org ++192.0.0.156 example.org ++192.0.0.157 example.org ++192.0.0.158 example.org ++192.0.0.159 example.org ++192.0.0.160 example.org ++192.0.0.161 example.org ++192.0.0.162 example.org ++192.0.0.163 example.org ++192.0.0.164 example.org ++192.0.0.165 example.org ++192.0.0.166 example.org ++192.0.0.167 example.org ++192.0.0.168 example.org ++192.0.0.169 example.org ++192.0.0.170 example.org ++192.0.0.171 example.org ++192.0.0.172 example.org ++192.0.0.173 example.org ++192.0.0.174 example.org ++192.0.0.175 example.org ++192.0.0.176 example.org ++192.0.0.177 example.org ++192.0.0.178 example.org ++192.0.0.179 example.org ++192.0.0.180 example.org ++192.0.0.181 example.org ++192.0.0.182 example.org ++192.0.0.183 example.org ++192.0.0.184 example.org ++192.0.0.185 example.org ++192.0.0.186 example.org ++192.0.0.187 example.org ++192.0.0.188 example.org ++192.0.0.189 example.org ++192.0.0.190 example.org ++192.0.0.191 example.org ++192.0.0.192 example.org ++192.0.0.193 example.org ++192.0.0.194 example.org ++192.0.0.195 example.org ++192.0.0.196 example.org ++192.0.0.197 example.org ++192.0.0.198 example.org ++192.0.0.199 example.org ++192.0.0.200 example.org ++192.0.0.201 example.org ++192.0.0.202 example.org ++192.0.0.203 example.org ++192.0.0.204 example.org ++192.0.0.205 example.org ++192.0.0.206 example.org ++192.0.0.207 example.org ++192.0.0.208 example.org ++192.0.0.209 example.org ++192.0.0.210 example.org ++192.0.0.211 example.org ++192.0.0.212 example.org ++192.0.0.213 example.org ++192.0.0.214 example.org ++192.0.0.215 example.org ++192.0.0.216 example.org ++192.0.0.217 example.org ++192.0.0.218 example.org ++192.0.0.219 example.org ++192.0.0.220 example.org ++192.0.0.221 example.org ++192.0.0.222 example.org ++192.0.0.223 example.org ++192.0.0.224 example.org ++192.0.0.225 example.org ++192.0.0.226 example.org ++192.0.0.227 example.org ++192.0.0.228 example.org ++192.0.0.229 example.org ++192.0.0.230 example.org ++192.0.0.231 example.org ++192.0.0.232 example.org ++192.0.0.233 example.org ++192.0.0.234 example.org ++192.0.0.235 example.org ++192.0.0.236 example.org ++192.0.0.237 example.org ++192.0.0.238 example.org ++192.0.0.239 example.org ++192.0.0.240 example.org ++192.0.0.241 example.org ++192.0.0.242 example.org ++192.0.0.243 example.org ++192.0.0.244 example.org ++192.0.0.245 example.org ++192.0.0.246 example.org ++192.0.0.247 example.org ++192.0.0.248 example.org ++192.0.0.249 example.org ++192.0.0.250 example.org ++192.0.0.251 example.org ++192.0.0.252 example.org ++192.0.0.253 example.org ++192.0.0.254 example.org ++192.0.1.1 example.org ++192.0.1.2 example.org ++192.0.1.3 example.org ++192.0.1.4 example.org ++192.0.1.5 example.org ++192.0.1.6 example.org ++192.0.1.7 example.org ++192.0.1.8 example.org ++192.0.1.9 example.org ++192.0.1.10 example.org ++192.0.1.11 example.org ++192.0.1.12 example.org ++192.0.1.13 example.org ++192.0.1.14 example.org ++192.0.1.15 example.org ++192.0.1.16 example.org ++192.0.1.17 example.org ++192.0.1.18 example.org ++192.0.1.19 example.org ++192.0.1.20 example.org ++192.0.1.21 example.org ++192.0.1.22 example.org ++192.0.1.23 example.org ++192.0.1.24 example.org ++192.0.1.25 example.org ++192.0.1.26 example.org ++192.0.1.27 example.org ++192.0.1.28 example.org ++192.0.1.29 example.org ++192.0.1.30 example.org ++192.0.1.31 example.org ++192.0.1.32 example.org ++192.0.1.33 example.org ++192.0.1.34 example.org ++192.0.1.35 example.org ++192.0.1.36 example.org ++192.0.1.37 example.org ++192.0.1.38 example.org ++192.0.1.39 example.org ++192.0.1.40 example.org ++192.0.1.41 example.org ++192.0.1.42 example.org ++192.0.1.43 example.org ++192.0.1.44 example.org ++192.0.1.45 example.org ++192.0.1.46 example.org ++192.0.1.47 example.org ++192.0.1.48 example.org ++192.0.1.49 example.org ++192.0.1.50 example.org ++192.0.1.51 example.org ++192.0.1.52 example.org ++192.0.1.53 example.org ++192.0.1.54 example.org ++192.0.1.55 example.org ++192.0.1.56 example.org ++192.0.1.57 example.org ++192.0.1.58 example.org ++192.0.1.59 example.org ++192.0.1.60 example.org ++192.0.1.61 example.org ++192.0.1.62 example.org ++192.0.1.63 example.org ++192.0.1.64 example.org ++192.0.1.65 example.org ++192.0.1.66 example.org ++192.0.1.67 example.org ++192.0.1.68 example.org ++192.0.1.69 example.org ++192.0.1.70 example.org ++192.0.1.71 example.org ++192.0.1.72 example.org ++192.0.1.73 example.org ++192.0.1.74 example.org ++192.0.1.75 example.org ++192.0.1.76 example.org ++192.0.1.77 example.org ++192.0.1.78 example.org ++192.0.1.79 example.org ++192.0.1.80 example.org ++192.0.1.81 example.org ++192.0.1.82 example.org ++192.0.1.83 example.org ++192.0.1.84 example.org ++192.0.1.85 example.org ++192.0.1.86 example.org ++192.0.1.87 example.org ++192.0.1.88 example.org ++192.0.1.89 example.org ++192.0.1.90 example.org ++192.0.1.91 example.org ++192.0.1.92 example.org ++192.0.1.93 example.org ++192.0.1.94 example.org ++192.0.1.95 example.org ++192.0.1.96 example.org ++192.0.1.97 example.org ++192.0.1.98 example.org ++192.0.1.99 example.org ++192.0.1.100 example.org ++192.0.1.101 example.org ++192.0.1.102 example.org ++192.0.1.103 example.org ++192.0.1.104 example.org ++192.0.1.105 example.org ++192.0.1.106 example.org ++192.0.1.107 example.org ++192.0.1.108 example.org ++192.0.1.109 example.org ++192.0.1.110 example.org ++192.0.1.111 example.org ++192.0.1.112 example.org ++192.0.1.113 example.org ++192.0.1.114 example.org ++192.0.1.115 example.org ++192.0.1.116 example.org ++192.0.1.117 example.org ++192.0.1.118 example.org ++192.0.1.119 example.org ++192.0.1.120 example.org ++192.0.1.121 example.org ++192.0.1.122 example.org ++192.0.1.123 example.org ++192.0.1.124 example.org ++192.0.1.125 example.org ++192.0.1.126 example.org ++192.0.1.127 example.org ++192.0.1.128 example.org ++192.0.1.129 example.org ++192.0.1.130 example.org ++192.0.1.131 example.org ++192.0.1.132 example.org ++192.0.1.133 example.org ++192.0.1.134 example.org ++192.0.1.135 example.org ++192.0.1.136 example.org ++192.0.1.137 example.org ++192.0.1.138 example.org ++192.0.1.139 example.org ++192.0.1.140 example.org ++192.0.1.141 example.org ++192.0.1.142 example.org ++192.0.1.143 example.org ++192.0.1.144 example.org ++192.0.1.145 example.org ++192.0.1.146 example.org ++192.0.1.147 example.org ++192.0.1.148 example.org ++192.0.1.149 example.org ++192.0.1.150 example.org ++192.0.1.151 example.org ++192.0.1.152 example.org ++192.0.1.153 example.org ++192.0.1.154 example.org ++192.0.1.155 example.org ++192.0.1.156 example.org ++192.0.1.157 example.org ++192.0.1.158 example.org ++192.0.1.159 example.org ++192.0.1.160 example.org ++192.0.1.161 example.org ++192.0.1.162 example.org ++192.0.1.163 example.org ++192.0.1.164 example.org ++192.0.1.165 example.org ++192.0.1.166 example.org ++192.0.1.167 example.org ++192.0.1.168 example.org ++192.0.1.169 example.org ++192.0.1.170 example.org ++192.0.1.171 example.org ++192.0.1.172 example.org ++192.0.1.173 example.org ++192.0.1.174 example.org ++192.0.1.175 example.org ++192.0.1.176 example.org ++192.0.1.177 example.org ++192.0.1.178 example.org ++192.0.1.179 example.org ++192.0.1.180 example.org ++192.0.1.181 example.org ++192.0.1.182 example.org ++192.0.1.183 example.org ++192.0.1.184 example.org ++192.0.1.185 example.org ++192.0.1.186 example.org ++192.0.1.187 example.org ++192.0.1.188 example.org ++192.0.1.189 example.org ++192.0.1.190 example.org ++192.0.1.191 example.org ++192.0.1.192 example.org ++192.0.1.193 example.org ++192.0.1.194 example.org ++192.0.1.195 example.org ++192.0.1.196 example.org ++192.0.1.197 example.org ++192.0.1.198 example.org ++192.0.1.199 example.org ++192.0.1.200 example.org ++192.0.1.201 example.org ++192.0.1.202 example.org ++192.0.1.203 example.org ++192.0.1.204 example.org ++192.0.1.205 example.org ++192.0.1.206 example.org ++192.0.1.207 example.org ++192.0.1.208 example.org ++192.0.1.209 example.org ++192.0.1.210 example.org ++192.0.1.211 example.org ++192.0.1.212 example.org ++192.0.1.213 example.org ++192.0.1.214 example.org ++192.0.1.215 example.org ++192.0.1.216 example.org ++192.0.1.217 example.org ++192.0.1.218 example.org ++192.0.1.219 example.org ++192.0.1.220 example.org ++192.0.1.221 example.org ++192.0.1.222 example.org ++192.0.1.223 example.org ++192.0.1.224 example.org ++192.0.1.225 example.org ++192.0.1.226 example.org ++192.0.1.227 example.org ++192.0.1.228 example.org ++192.0.1.229 example.org ++192.0.1.230 example.org ++192.0.1.231 example.org ++192.0.1.232 example.org ++192.0.1.233 example.org ++192.0.1.234 example.org ++192.0.1.235 example.org ++192.0.1.236 example.org ++192.0.1.237 example.org ++192.0.1.238 example.org ++192.0.1.239 example.org ++192.0.1.240 example.org ++192.0.1.241 example.org ++192.0.1.242 example.org ++192.0.1.243 example.org ++192.0.1.244 example.org ++192.0.1.245 example.org ++192.0.1.246 example.org ++192.0.1.247 example.org ++192.0.1.248 example.org ++192.0.1.249 example.org ++192.0.1.250 example.org ++192.0.1.251 example.org ++192.0.1.252 example.org ++192.0.1.253 example.org ++192.0.1.254 example.org +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index 43dfc6739e350a58..f391dc0a59849aab 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -458,11 +458,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + + if (name != NULL) + { +- at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); +- at->family = AF_UNSPEC; +- at->scopeid = 0; +- at->next = NULL; +- + if (req->ai_flags & AI_IDN) + { + char *out; +@@ -473,13 +468,21 @@ gaih_inet (const char *name, const struct gaih_service *service, + malloc_name = true; + } + +- if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0) ++ uint32_t addr[4]; ++ if (__inet_aton_exact (name, (struct in_addr *) addr) != 0) + { ++ at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); ++ at->scopeid = 0; ++ at->next = NULL; ++ + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) +- at->family = AF_INET; ++ { ++ memcpy (at->addr, addr, sizeof (at->addr)); ++ at->family = AF_INET; ++ } + else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) + { +- at->addr[3] = at->addr[0]; ++ at->addr[3] = addr[0]; + at->addr[2] = htonl (0xffff); + at->addr[1] = 0; + at->addr[0] = 0; +@@ -493,49 +496,62 @@ gaih_inet (const char *name, const struct gaih_service *service, + + if (req->ai_flags & AI_CANONNAME) + canon = name; ++ ++ goto process_list; + } +- else if (at->family == AF_UNSPEC) ++ ++ char *scope_delim = strchr (name, SCOPE_DELIMITER); ++ int e; ++ ++ if (scope_delim == NULL) ++ e = inet_pton (AF_INET6, name, addr); ++ else ++ e = __inet_pton_length (AF_INET6, name, scope_delim - name, addr); ++ ++ if (e > 0) + { +- char *scope_delim = strchr (name, SCOPE_DELIMITER); +- int e; +- if (scope_delim == NULL) +- e = inet_pton (AF_INET6, name, at->addr); ++ at = alloca_account (sizeof (struct gaih_addrtuple), ++ alloca_used); ++ at->scopeid = 0; ++ at->next = NULL; ++ ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) ++ { ++ memcpy (at->addr, addr, sizeof (at->addr)); ++ at->family = AF_INET6; ++ } ++ else if (req->ai_family == AF_INET ++ && IN6_IS_ADDR_V4MAPPED (addr)) ++ { ++ at->addr[0] = addr[3]; ++ at->addr[1] = addr[1]; ++ at->addr[2] = addr[2]; ++ at->addr[3] = addr[3]; ++ at->family = AF_INET; ++ } + else +- e = __inet_pton_length (AF_INET6, name, scope_delim - name, +- at->addr); +- if (e > 0) + { +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) +- at->family = AF_INET6; +- else if (req->ai_family == AF_INET +- && IN6_IS_ADDR_V4MAPPED (at->addr)) +- { +- at->addr[0] = at->addr[3]; +- at->family = AF_INET; +- } +- else +- { +- result = -EAI_ADDRFAMILY; +- goto free_and_return; +- } +- +- if (scope_delim != NULL +- && __inet6_scopeid_pton ((struct in6_addr *) at->addr, +- scope_delim + 1, +- &at->scopeid) != 0) +- { +- result = -EAI_NONAME; +- goto free_and_return; +- } ++ result = -EAI_ADDRFAMILY; ++ goto free_and_return; ++ } + +- if (req->ai_flags & AI_CANONNAME) +- canon = name; ++ if (scope_delim != NULL ++ && __inet6_scopeid_pton ((struct in6_addr *) at->addr, ++ scope_delim + 1, ++ &at->scopeid) != 0) ++ { ++ result = -EAI_NONAME; ++ goto free_and_return; + } ++ ++ if (req->ai_flags & AI_CANONNAME) ++ canon = name; ++ ++ goto process_list; + } + +- if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0) ++ if ((req->ai_flags & AI_NUMERICHOST) == 0) + { +- struct gaih_addrtuple **pat = &at; + int no_data = 0; + int no_inet6_data = 0; + nss_action_list nip; +@@ -543,6 +559,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + enum nss_status status = NSS_STATUS_UNAVAIL; + int no_more; + struct resolv_context *res_ctx = NULL; ++ bool do_merge = false; + + /* If we do not have to look for IPv6 addresses or the canonical + name, use the simple, old functions, which do not support +@@ -579,7 +596,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + result = -EAI_MEMORY; + goto free_and_return; + } +- *pat = addrmem; ++ at = addrmem; + } + else + { +@@ -632,6 +649,8 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + + struct gaih_addrtuple *addrfree = addrmem; ++ struct gaih_addrtuple **pat = &at; ++ + for (int i = 0; i < air->naddrs; ++i) + { + socklen_t size = (air->family[i] == AF_INET +@@ -695,12 +714,6 @@ gaih_inet (const char *name, const struct gaih_service *service, + + free (air); + +- if (at->family == AF_UNSPEC) +- { +- result = -EAI_NONAME; +- goto free_and_return; +- } +- + goto process_list; + } + else if (err == 0) +@@ -732,6 +745,22 @@ gaih_inet (const char *name, const struct gaih_service *service, + + while (!no_more) + { ++ /* Always start afresh; continue should discard previous results ++ and the hosts database does not support merge. */ ++ at = NULL; ++ free (canonbuf); ++ free (addrmem); ++ canon = canonbuf = NULL; ++ addrmem = NULL; ++ got_ipv6 = false; ++ ++ if (do_merge) ++ { ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ break; ++ } ++ + no_data = 0; + nss_gethostbyname4_r *fct4 = NULL; + +@@ -744,12 +773,14 @@ gaih_inet (const char *name, const struct gaih_service *service, + { + while (1) + { +- status = DL_CALL_FCT (fct4, (name, pat, ++ status = DL_CALL_FCT (fct4, (name, &at, + tmpbuf->data, tmpbuf->length, + &errno, &h_errno, + NULL)); + if (status == NSS_STATUS_SUCCESS) + break; ++ /* gethostbyname4_r may write into AT, so reset it. */ ++ at = NULL; + if (status != NSS_STATUS_TRYAGAIN + || errno != ERANGE || h_errno != NETDB_INTERNAL) + { +@@ -774,7 +805,9 @@ gaih_inet (const char *name, const struct gaih_service *service, + no_data = 1; + + if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL) +- canon = (*pat)->name; ++ canon = at->name; ++ ++ struct gaih_addrtuple **pat = &at; + + while (*pat != NULL) + { +@@ -826,6 +859,8 @@ gaih_inet (const char *name, const struct gaih_service *service, + + if (fct != NULL) + { ++ struct gaih_addrtuple **pat = &at; ++ + if (req->ai_family == AF_INET6 + || req->ai_family == AF_UNSPEC) + { +@@ -899,6 +934,10 @@ gaih_inet (const char *name, const struct gaih_service *service, + if (nss_next_action (nip, status) == NSS_ACTION_RETURN) + break; + ++ /* The hosts database does not support MERGE. */ ++ if (nss_next_action (nip, status) == NSS_ACTION_MERGE) ++ do_merge = true; ++ + nip++; + if (nip->module == NULL) + no_more = -1; +@@ -930,7 +969,7 @@ gaih_inet (const char *name, const struct gaih_service *service, + } + + process_list: +- if (at->family == AF_UNSPEC) ++ if (at == NULL) + { + result = -EAI_NONAME; + goto free_and_return; diff --git a/SOURCES/glibc-RHEL-2999.patch b/SOURCES/glibc-RHEL-2999.patch new file mode 100644 index 0000000..e072c71 --- /dev/null +++ b/SOURCES/glibc-RHEL-2999.patch @@ -0,0 +1,164 @@ +This patch was developed under embargo and cannot reference an upstream +commit. To find the associated commit please review the upstream git +log for CVE-2023-4911 to identify the relevant commits. + +Author: Siddhesh Poyarekar +Date: Tue Sep 19 11:52:44 2023 -0400 + + tunables: Terminate if end of input is reached (CVE-2023-4911) + + The string parsing routine may end up writing beyond bounds of tunestr + if the input tunable string is malformed, of the form name=name=val. + This gets processed twice, first as name=name=val and next as name=val, + resulting in tunestr being name=name=val:name=val, thus overflowing + tunestr. + + Terminate the parsing loop at the first instance itself so that tunestr + does not overflow. + + This also fixes up tst-env-setuid-tunables to actually handle failures + correct and add new tests to validate the fix for this CVE. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: Carlos O'Donell +--- +Tested on x86_64. + + NEWS | 5 +++++ + elf/dl-tunables.c | 17 ++++++++++------- + elf/tst-env-setuid-tunables.c | 36 +++++++++++++++++++++++++++-------- + 3 files changed, 43 insertions(+), 15 deletions(-) + +Conflicts: + NEWS + (Dropped) + elf/tst-env-setuid-tunables.c + (Trivial HAVE_TUNABLES conflict) + +diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c +index 8009e54ee5db32be..837474b5044cb5d7 100644 +--- a/elf/dl-tunables.c ++++ b/elf/dl-tunables.c +@@ -188,11 +188,7 @@ parse_tunables (char *tunestr, char *valstring) + /* If we reach the end of the string before getting a valid name-value + pair, bail out. */ + if (p[len] == '\0') +- { +- if (__libc_enable_secure) +- tunestr[off] = '\0'; +- return; +- } ++ break; + + /* We did not find a valid name-value pair before encountering the + colon. */ +@@ -252,9 +248,16 @@ parse_tunables (char *tunestr, char *valstring) + } + } + +- if (p[len] != '\0') +- p += len + 1; ++ /* We reached the end while processing the tunable string. */ ++ if (p[len] == '\0') ++ break; ++ ++ p += len + 1; + } ++ ++ /* Terminate tunestr before we leave. */ ++ if (__libc_enable_secure) ++ tunestr[off] = '\0'; + } + #endif + +diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c +index 05619c9adc8b2698..cd4e84364074c613 100644 +--- a/elf/tst-env-setuid-tunables.c ++++ b/elf/tst-env-setuid-tunables.c +@@ -52,6 +52,8 @@ const char *teststrings[] = + "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096", + "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096", + "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096", ++ "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096", ++ "glibc.malloc.check=2", + "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2", + "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096", + ":glibc.malloc.garbage=2:glibc.malloc.check=1", +@@ -70,6 +72,8 @@ const char *resultstrings[] = + "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096", + "glibc.malloc.mmap_threshold=4096", + "glibc.malloc.mmap_threshold=4096", ++ "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096", ++ "", + "", + "", + "", +@@ -84,11 +88,18 @@ test_child (int off) + const char *val = getenv ("GLIBC_TUNABLES"); + + #if HAVE_TUNABLES ++ printf (" [%d] GLIBC_TUNABLES is %s\n", off, val); ++ fflush (stdout); + if (val != NULL && strcmp (val, resultstrings[off]) == 0) + return 0; + + if (val != NULL) +- printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val); ++ printf (" [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n", ++ off, val, resultstrings[off]); ++ else ++ printf (" [%d] GLIBC_TUNABLES environment variable absent\n", off); ++ ++ fflush (stdout); + + return 1; + #else +@@ -117,21 +128,26 @@ do_test (int argc, char **argv) + if (ret != 0) + exit (1); + +- exit (EXIT_SUCCESS); ++ /* Special return code to make sure that the child executed all the way ++ through. */ ++ exit (42); + } + else + { +- int ret = 0; +- + /* Spawn tests. */ + for (int i = 0; i < array_length (teststrings); i++) + { + char buf[INT_BUFSIZE_BOUND (int)]; + +- printf ("Spawned test for %s (%d)\n", teststrings[i], i); ++ printf ("[%d] Spawned test for %s\n", i, teststrings[i]); + snprintf (buf, sizeof (buf), "%d\n", i); ++ fflush (stdout); + if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0) +- exit (1); ++ { ++ printf (" [%d] Failed to set GLIBC_TUNABLES: %m", i); ++ support_record_failure (); ++ continue; ++ } + + int status = support_capture_subprogram_self_sgid (buf); + +@@ -139,9 +155,14 @@ do_test (int argc, char **argv) + if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) + return EXIT_UNSUPPORTED; + +- ret |= status; ++ if (WEXITSTATUS (status) != 42) ++ { ++ printf (" [%d] child failed with status %d\n", i, ++ WEXITSTATUS (status)); ++ support_record_failure (); ++ } + } +- return ret; ++ return 0; + } + } + diff --git a/SOURCES/glibc-c-utf8-locale-2.patch b/SOURCES/glibc-c-utf8-locale-2.patch deleted file mode 100644 index 7064b8e..0000000 --- a/SOURCES/glibc-c-utf8-locale-2.patch +++ /dev/null @@ -1,1437 +0,0 @@ -commit 466f2be6c08070e9113ae2fdc7acd5d8828cba50 -Author: Carlos O'Donell -Date: Wed Sep 1 15:19:19 2021 -0400 - - Add generic C.UTF-8 locale (Bug 17318) - - We add a new C.UTF-8 locale. This locale is not builtin to glibc, but - is provided as a distinct locale. The locale provides full support for - UTF-8 and this includes full code point sorting via STRCMP-based - collation (strcmp or wcscmp). - - The collation uses a new keyword 'codepoint_collation' which drops all - collation rules and generates an empty zero rules collation to enable - STRCMP usage in collation. This ensures that we get full code point - sorting for C.UTF-8 with a minimal 1406 bytes of overhead (LC_COLLATE - structure information and ASCII collating tables). - - The new locale is added to SUPPORTED. Minimal test data for specific - code points (minus those not supported by collate-test) is provided in - C.UTF-8.in, and this verifies code point sorting is working reasonably - across the range. The locale was tested manually with the full set of - code points without failure. - - The locale is harmonized with locales already shipping in various - downstream distributions. A new tst-iconv9 test is added which verifies - the C.UTF-8 locale is generally usable. - - Testing for fnmatch, regexec, and recomp is provided by extending - bug-regex1, bugregex19, bug-regex4, bug-regex6, transbug, tst-fnmatch, - tst-regcomp-truncated, and tst-regex to use C.UTF-8. - - Tested on x86_64 or i686 without regression. - - Reviewed-by: Florian Weimer - -diff --git a/iconv/Makefile b/iconv/Makefile -index 07d77c9ecaafba1f..9993f2d3f3cd7498 100644 ---- a/iconv/Makefile -+++ b/iconv/Makefile -@@ -43,8 +43,19 @@ CFLAGS-charmap.c += -DCHARMAP_PATH='"$(i18ndir)/charmaps"' \ - CFLAGS-linereader.c += -DNO_TRANSLITERATION - CFLAGS-simple-hash.c += -I../locale - --tests = tst-iconv1 tst-iconv2 tst-iconv3 tst-iconv4 tst-iconv5 tst-iconv6 \ -- tst-iconv7 tst-iconv8 tst-iconv-mt tst-iconv-opt -+tests = \ -+ tst-iconv1 \ -+ tst-iconv2 \ -+ tst-iconv3 \ -+ tst-iconv4 \ -+ tst-iconv5 \ -+ tst-iconv6 \ -+ tst-iconv7 \ -+ tst-iconv8 \ -+ tst-iconv9 \ -+ tst-iconv-mt \ -+ tst-iconv-opt \ -+ # tests - - others = iconv_prog iconvconfig - install-others-programs = $(inst_bindir)/iconv -@@ -83,10 +94,15 @@ endif - include ../Rules - - ifeq ($(run-built-tests),yes) --LOCALES := en_US.UTF-8 -+# We have to generate locales (list sorted alphabetically) -+LOCALES := \ -+ C.UTF-8 \ -+ en_US.UTF-8 \ -+ # LOCALES - include ../gen-locales.mk - - $(objpfx)tst-iconv-opt.out: $(gen-locales) -+$(objpfx)tst-iconv9.out: $(gen-locales) - endif - - $(inst_bindir)/iconv: $(objpfx)iconv_prog $(+force) -diff --git a/iconv/tst-iconv9.c b/iconv/tst-iconv9.c -new file mode 100644 -index 0000000000000000..c46b1833d87b8e55 ---- /dev/null -+++ b/iconv/tst-iconv9.c -@@ -0,0 +1,87 @@ -+/* Verify that using C.UTF-8 works. -+ -+ Copyright (C) 2021 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 -+ -+/* This test does two things: -+ (1) Verify that we have likely included translit_combining in C.UTF-8. -+ (2) Verify default_missing is '?' as expected. */ -+ -+/* ISO-8859-1 encoding of "für". */ -+char iso88591_in[] = { 0x66, 0xfc, 0x72, 0x0 }; -+/* ASCII transliteration is "fur" with C.UTF-8 translit_combining. */ -+char ascii_exp[] = { 0x66, 0x75, 0x72, 0x0 }; -+ -+/* First 3-byte UTF-8 code point. */ -+char utf8_in[] = { 0xe0, 0xa0, 0x80, 0x0 }; -+/* There is no ASCII transliteration for SAMARITAN LETTER ALAF -+ so we get default_missing used which is '?'. */ -+char default_missing_exp[] = { 0x3f, 0x0 }; -+ -+static int -+do_test (void) -+{ -+ char ascii_out[5]; -+ iconv_t cd; -+ char *inbuf; -+ char *outbuf; -+ size_t inbytes; -+ size_t outbytes; -+ size_t n; -+ -+ /* The C.UTF-8 locale should include translit_combining, which provides -+ the transliteration for "LATIN SMALL LETTER U WITH DIAERESIS" which -+ is not provided by locale/C-translit.h.in. */ -+ xsetlocale (LC_ALL, "C.UTF-8"); -+ -+ /* From ISO-8859-1 to ASCII. */ -+ cd = iconv_open ("ASCII//TRANSLIT,IGNORE", "ISO-8859-1"); -+ TEST_VERIFY (cd != (iconv_t) -1); -+ inbuf = iso88591_in; -+ inbytes = 3; -+ outbuf = ascii_out; -+ outbytes = 3; -+ n = iconv (cd, &inbuf, &inbytes, &outbuf, &outbytes); -+ TEST_VERIFY (n != -1); -+ *outbuf = '\0'; -+ TEST_COMPARE_BLOB (ascii_out, 3, ascii_exp, 3); -+ TEST_VERIFY (iconv_close (cd) == 0); -+ -+ /* From UTF-8 to ASCII. */ -+ cd = iconv_open ("ASCII//TRANSLIT,IGNORE", "UTF-8"); -+ TEST_VERIFY (cd != (iconv_t) -1); -+ inbuf = utf8_in; -+ inbytes = 3; -+ outbuf = ascii_out; -+ outbytes = 3; -+ n = iconv (cd, &inbuf, &inbytes, &outbuf, &outbytes); -+ TEST_VERIFY (n != -1); -+ *outbuf = '\0'; -+ TEST_COMPARE_BLOB (ascii_out, 1, default_missing_exp, 1); -+ TEST_VERIFY (iconv_close (cd) == 0); -+ -+ return 0; -+} -+ -+#include -diff --git a/localedata/C.UTF-8.in b/localedata/C.UTF-8.in -new file mode 100644 -index 0000000000000000..c31dcc2aa045ee61 ---- /dev/null -+++ b/localedata/C.UTF-8.in -@@ -0,0 +1,157 @@ -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+ ; -+! ; -+" ; -+# ; -+$ ; -+% ; -+& ; -+' ; -+) ; -+* ; -++ ; -+, ; -+- ; -+. ; -+/ ; -+0 ; -+1 ; -+2 ; -+3 ; -+4 ; -+5 ; -+6 ; -+7 ; -+8 ; -+9 ; -+< ; -+= ; -+> ; -+? ; -+@ ; -+A ; -+B ; -+C ; -+D ; -+E ; -+F ; -+G ; -+H ; -+I ; -+J ; -+K ; -+L ; -+M ; -+N ; -+O ; -+P ; -+Q ; -+R ; -+S ; -+T ; -+U ; -+V ; -+W ; -+X ; -+Y ; -+Z ; -+[ ; -+\ ; -+] ; -+^ ; -+_ ; -+` ; -+a ; -+b ; -+c ; -+d ; -+e ; -+f ; -+g ; -+h ; -+i ; -+j ; -+k ; -+l ; -+m ; -+n ; -+o ; -+p ; -+q ; -+r ; -+s ; -+t ; -+u ; -+v ; -+w ; -+x ; -+y ; -+z ; -+{ ; -+| ; -+} ; -+~ ; -+ ; -+€ ; -+ÿ ; -+Ā ; -+࿿ ; -+က ; -+� ; -+￿ ; -+𐀀 ; -+🿿 ; -+𠀀 ; -+𯿿 ; -+𰀀 ; -+𿿾 ; -+񀀀 ; -+񏿿 ; -+񐀀 ; -+񟿿 ; -+񠀀 ; -+񯿿 ; -+񰀀 ; -+񿿿 ; -+򀀀 ; -+򏿿 ; -+򐀀 ; -+򟿿 ; -+򠀀 ; -+򯿿 ; -+򰀀 ; -+򿿿 ; -+󀀁 ; -+󏿌 ; -+󐀎 ; -+󟿿 ; -+󠀁 ; -+󯿿 ; -+󰀁 ; -+󿿿 ; -+􀀁 ; -+􏿿 ; -diff --git a/localedata/Makefile b/localedata/Makefile -index 0341528b0407ae3b..c9dd5a954e8194cc 100644 ---- a/localedata/Makefile -+++ b/localedata/Makefile -@@ -47,6 +47,7 @@ test-input := \ - bg_BG.UTF-8 \ - br_FR.UTF-8 \ - bs_BA.UTF-8 \ -+ C.UTF-8 \ - ckb_IQ.UTF-8 \ - cmn_TW.UTF-8 \ - crh_UA.UTF-8 \ -@@ -206,6 +207,7 @@ LOCALES := \ - bg_BG.UTF-8 \ - br_FR.UTF-8 \ - bs_BA.UTF-8 \ -+ C.UTF-8 \ - ckb_IQ.UTF-8 \ - cmn_TW.UTF-8 \ - crh_UA.UTF-8 \ -diff --git a/localedata/SUPPORTED b/localedata/SUPPORTED -index 34f7a7c3fe2b6526..546ce6cea16a8fdb 100644 ---- a/localedata/SUPPORTED -+++ b/localedata/SUPPORTED -@@ -79,6 +79,7 @@ brx_IN/UTF-8 \ - bs_BA.UTF-8/UTF-8 \ - bs_BA/ISO-8859-2 \ - byn_ER/UTF-8 \ -+C.UTF-8/UTF-8 \ - ca_AD.UTF-8/UTF-8 \ - ca_AD/ISO-8859-15 \ - ca_ES.UTF-8/UTF-8 \ -diff --git a/localedata/locales/C b/localedata/locales/C -new file mode 100644 -index 0000000000000000..ca801c79cf7e953e ---- /dev/null -+++ b/localedata/locales/C -@@ -0,0 +1,194 @@ -+escape_char / -+comment_char % -+% Locale for C locale in UTF-8 -+ -+LC_IDENTIFICATION -+title "C locale" -+source "" -+address "" -+contact "" -+email "bug-glibc-locales@gnu.org" -+tel "" -+fax "" -+language "" -+territory "" -+revision "2.0" -+date "2020-06-28" -+category "i18n:2012";LC_IDENTIFICATION -+category "i18n:2012";LC_CTYPE -+category "i18n:2012";LC_COLLATE -+category "i18n:2012";LC_TIME -+category "i18n:2012";LC_NUMERIC -+category "i18n:2012";LC_MONETARY -+category "i18n:2012";LC_MESSAGES -+category "i18n:2012";LC_PAPER -+category "i18n:2012";LC_NAME -+category "i18n:2012";LC_ADDRESS -+category "i18n:2012";LC_TELEPHONE -+category "i18n:2012";LC_MEASUREMENT -+END LC_IDENTIFICATION -+ -+LC_CTYPE -+% Include only the i18n character type classes without any of the -+% transliteration that i18n uses by default. -+copy "i18n_ctype" -+ -+% Include the neutral transliterations. The builtin C and -+% POSIX locales have +1600 transliterations that are built into -+% the locales, and these are a superset of those. -+translit_start -+include "translit_neutral";"" -+% We must use '?' for default_missing because the transliteration -+% framework includes it directly into the output and so it must -+% be compatible with ASCII if that is the target character set. -+default_missing -+translit_end -+ -+% Include the transliterations that can convert combined characters. -+% These are generally expected by users. -+translit_start -+include "translit_combining";"" -+translit_end -+ -+END LC_CTYPE -+ -+LC_COLLATE -+% The keyword 'codepoint_collation' in any part of any LC_COLLATE -+% immediately discards all collation information and causes the -+% locale to use strcmp/wcscmp for collation comparison. This is -+% exactly what is needed for C (ASCII) or C.UTF-8. -+codepoint_collation -+END LC_COLLATE -+ -+LC_MONETARY -+ -+% This is the 14652 i18n fdcc-set definition for the LC_MONETARY -+% category (except for the int_curr_symbol and currency_symbol, they are -+% empty in the 14652 i18n fdcc-set definition and also empty in -+% glibc/locale/C-monetary.c.). -+int_curr_symbol "" -+currency_symbol "" -+mon_decimal_point "." -+mon_thousands_sep "" -+mon_grouping -1 -+positive_sign "" -+negative_sign "-" -+int_frac_digits -1 -+frac_digits -1 -+p_cs_precedes -1 -+int_p_sep_by_space -1 -+p_sep_by_space -1 -+n_cs_precedes -1 -+int_n_sep_by_space -1 -+n_sep_by_space -1 -+p_sign_posn -1 -+n_sign_posn -1 -+% -+END LC_MONETARY -+ -+LC_NUMERIC -+% This is the POSIX Locale definition for -+% the LC_NUMERIC category. -+% -+decimal_point "." -+thousands_sep "" -+grouping -1 -+END LC_NUMERIC -+ -+LC_TIME -+% This is the POSIX Locale definition for the LC_TIME category with the -+% exception that time is per ISO 8601 and 24-hour. -+% -+% Abbreviated weekday names (%a) -+abday "Sun";"Mon";"Tue";"Wed";"Thu";"Fri";"Sat" -+ -+% Full weekday names (%A) -+day "Sunday";"Monday";"Tuesday";"Wednesday";"Thursday";/ -+ "Friday";"Saturday" -+ -+% Abbreviated month names (%b) -+abmon "Jan";"Feb";"Mar";"Apr";"May";"Jun";"Jul";"Aug";"Sep";/ -+ "Oct";"Nov";"Dec" -+ -+% Full month names (%B) -+mon "January";"February";"March";"April";"May";"June";"July";/ -+ "August";"September";"October";"November";"December" -+ -+% Week description, consists of three fields: -+% 1. Number of days in a week. -+% 2. Gregorian date that is a first weekday (19971130 for Sunday, 19971201 for Monday). -+% 3. The weekday number to be contained in the first week of the year. -+% -+% ISO 8601 conforming applications should use the values 7, 19971201 (a -+% Monday), and 4 (Thursday), respectively. -+week 7;19971201;4 -+first_weekday 1 -+first_workday 2 -+ -+% Appropriate date and time representation (%c) -+d_t_fmt "%a %b %e %H:%M:%S %Y" -+ -+% Appropriate date representation (%x) -+d_fmt "%m/%d/%y" -+ -+% Appropriate time representation (%X) -+t_fmt "%H:%M:%S" -+ -+% Appropriate AM/PM time representation (%r) -+t_fmt_ampm "%I:%M:%S %p" -+ -+% Equivalent of AM/PM (%p) -+am_pm "AM";"PM" -+ -+% Appropriate date representation (date(1)) -+date_fmt "%a %b %e %H:%M:%S %Z %Y" -+END LC_TIME -+ -+LC_MESSAGES -+% This is the POSIX Locale definition for -+% the LC_NUMERIC category. -+% -+yesexpr "^[yY]" -+noexpr "^[nN]" -+yesstr "Yes" -+nostr "No" -+END LC_MESSAGES -+ -+LC_PAPER -+% This is the ISO/IEC 14652 "i18n" definition for -+% the LC_PAPER category. -+% (A4 paper, this is also used in the built in C/POSIX -+% locale in glibc/locale/C-paper.c) -+height 297 -+width 210 -+END LC_PAPER -+ -+LC_NAME -+% This is the ISO/IEC 14652 "i18n" definition for -+% the LC_NAME category. -+% (also used in the built in C/POSIX locale in glibc/locale/C-name.c) -+name_fmt "%p%t%g%t%m%t%f" -+END LC_NAME -+ -+LC_ADDRESS -+% This is the ISO/IEC 14652 "i18n" definition for -+% the LC_ADDRESS category. -+% (also used in the built in C/POSIX locale in glibc/locale/C-address.c) -+postal_fmt "%a%N%f%N%d%N%b%N%s %h %e %r%N%C-%z %T%N%c%N" -+END LC_ADDRESS -+ -+LC_TELEPHONE -+% This is the ISO/IEC 14652 "i18n" definition for -+% the LC_TELEPHONE category. -+% "+%c %a %l" -+tel_int_fmt "+%c %a %l" -+% (also used in the built in C/POSIX locale in glibc/locale/C-telephone.c) -+END LC_TELEPHONE -+ -+LC_MEASUREMENT -+% This is the ISO/IEC 14652 "i18n" definition for -+% the LC_MEASUREMENT category. -+% (same as in the built in C/POSIX locale in glibc/locale/C-measurement.c) -+%metric -+measurement 1 -+END LC_MEASUREMENT -diff --git a/posix/Makefile b/posix/Makefile -index 059efb3cd2706cbe..a5229777eeb0e067 100644 ---- a/posix/Makefile -+++ b/posix/Makefile -@@ -190,9 +190,19 @@ $(objpfx)wordexp-tst.out: wordexp-tst.sh $(objpfx)wordexp-test - $(evaluate-test) - endif - --LOCALES := cs_CZ.UTF-8 da_DK.ISO-8859-1 de_DE.ISO-8859-1 de_DE.UTF-8 \ -- en_US.UTF-8 es_US.ISO-8859-1 es_US.UTF-8 ja_JP.EUC-JP tr_TR.UTF-8 \ -- cs_CZ.ISO-8859-2 -+LOCALES := \ -+ cs_CZ.ISO-8859-2 \ -+ cs_CZ.UTF-8 \ -+ C.UTF-8 \ -+ da_DK.ISO-8859-1 \ -+ de_DE.ISO-8859-1 \ -+ de_DE.UTF-8 \ -+ en_US.UTF-8 \ -+ es_US.ISO-8859-1 \ -+ es_US.UTF-8 \ -+ ja_JP.EUC-JP \ -+ tr_TR.UTF-8 \ -+ # LOCALES - include ../gen-locales.mk - - $(objpfx)bug-regex1.out: $(gen-locales) -diff --git a/posix/bug-regex1.c b/posix/bug-regex1.c -index 38eb543951862492..7e9f4ec430a95631 100644 ---- a/posix/bug-regex1.c -+++ b/posix/bug-regex1.c -@@ -41,6 +41,26 @@ main (void) - puts (" -> OK"); - } - -+ puts ("in C.UTF-8 locale"); -+ setlocale (LC_ALL, "C.UTF-8"); -+ s = re_compile_pattern ("[an\371]*n", 7, ®ex); -+ if (s != NULL) -+ { -+ puts ("re_compile_pattern return non-NULL value"); -+ result = 1; -+ } -+ else -+ { -+ match = re_match (®ex, "an", 2, 0, ®s); -+ if (match != 2) -+ { -+ printf ("re_match returned %d, expected 2\n", match); -+ result = 1; -+ } -+ else -+ puts (" -> OK"); -+ } -+ - puts ("in de_DE.ISO-8859-1 locale"); - setlocale (LC_ALL, "de_DE.ISO-8859-1"); - s = re_compile_pattern ("[an]*n", 7, ®ex); -diff --git a/posix/bug-regex19.c b/posix/bug-regex19.c -index b3fee0a7302c3263..e00ff60a14f994bf 100644 ---- a/posix/bug-regex19.c -+++ b/posix/bug-regex19.c -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - - #define BRE RE_SYNTAX_POSIX_BASIC - #define ERE RE_SYNTAX_POSIX_EXTENDED -@@ -407,8 +408,8 @@ do_mb_tests (const struct test_s *test) - return 0; - } - --int --main (void) -+static int -+do_test (void) - { - size_t i; - int ret = 0; -@@ -417,20 +418,17 @@ main (void) - - for (i = 0; i < sizeof (tests) / sizeof (tests[0]); ++i) - { -- if (setlocale (LC_ALL, "de_DE.ISO-8859-1") == NULL) -- { -- puts ("setlocale de_DE.ISO-8859-1 failed"); -- ret = 1; -- } -+ xsetlocale (LC_ALL, "de_DE.ISO-8859-1"); - ret |= do_one_test (&tests[i], ""); -- if (setlocale (LC_ALL, "de_DE.UTF-8") == NULL) -- { -- puts ("setlocale de_DE.UTF-8 failed"); -- ret = 1; -- } -+ xsetlocale (LC_ALL, "de_DE.UTF-8"); -+ ret |= do_one_test (&tests[i], "UTF-8 "); -+ ret |= do_mb_tests (&tests[i]); -+ xsetlocale (LC_ALL, "C.UTF-8"); - ret |= do_one_test (&tests[i], "UTF-8 "); - ret |= do_mb_tests (&tests[i]); - } - - return ret; - } -+ -+#include -diff --git a/posix/bug-regex4.c b/posix/bug-regex4.c -index 8d5ae11567889301..6475833c525176b2 100644 ---- a/posix/bug-regex4.c -+++ b/posix/bug-regex4.c -@@ -32,8 +32,33 @@ main (void) - - memset (®ex, '\0', sizeof (regex)); - -+ printf ("INFO: Checking C.\n"); - setlocale (LC_ALL, "C"); - -+ s = re_compile_pattern ("ab[cde]", 7, ®ex); -+ if (s != NULL) -+ { -+ puts ("re_compile_pattern returned non-NULL value"); -+ result = 1; -+ } -+ else -+ { -+ match[0] = re_search_2 (®ex, "xyabez", 6, "", 0, 1, 5, NULL, 6); -+ match[1] = re_search_2 (®ex, NULL, 0, "abc", 3, 0, 3, NULL, 3); -+ match[2] = re_search_2 (®ex, "xya", 3, "bd", 2, 2, 3, NULL, 5); -+ if (match[0] != 2 || match[1] != 0 || match[2] != 2) -+ { -+ printf ("re_search_2 returned %d,%d,%d, expected 2,0,2\n", -+ match[0], match[1], match[2]); -+ result = 1; -+ } -+ else -+ puts (" -> OK"); -+ } -+ -+ printf ("INFO: Checking C.UTF-8.\n"); -+ setlocale (LC_ALL, "C.UTF-8"); -+ - s = re_compile_pattern ("ab[cde]", 7, ®ex); - if (s != NULL) - { -diff --git a/posix/bug-regex6.c b/posix/bug-regex6.c -index 2bdf2126a49ee99b..0929b69b83c91e5e 100644 ---- a/posix/bug-regex6.c -+++ b/posix/bug-regex6.c -@@ -30,7 +30,7 @@ main (int argc, char *argv[]) - regex_t re; - regmatch_t mat[10]; - int i, j, ret = 0; -- const char *locales[] = { "C", "de_DE.UTF-8" }; -+ const char *locales[] = { "C", "C.UTF-8", "de_DE.UTF-8" }; - const char *string = "http://www.regex.com/pattern/matching.html#intro"; - regmatch_t expect[10] = { - { 0, 48 }, { 0, 5 }, { 0, 4 }, { 5, 20 }, { 7, 20 }, { 20, 42 }, -diff --git a/posix/transbug.c b/posix/transbug.c -index d0983b4d44d04fd2..b240177cf72326ff 100644 ---- a/posix/transbug.c -+++ b/posix/transbug.c -@@ -116,16 +116,32 @@ do_test (void) - static const char lower[] = "[[:lower:]]+"; - static const char upper[] = "[[:upper:]]+"; - struct re_registers regs[4]; -+ int result = 0; - -+#define CHECK(exp) \ -+ if (exp) { puts (#exp); result = 1; } -+ -+ printf ("INFO: Checking C.\n"); - setlocale (LC_ALL, "C"); - - (void) re_set_syntax (RE_SYNTAX_GNU_AWK); - -- int result; --#define CHECK(exp) \ -- if (exp) { puts (#exp); result = 1; } -+ result |= run_test (lower, regs); -+ result |= run_test (upper, ®s[2]); -+ if (! result) -+ { -+ CHECK (regs[0].start[0] != regs[2].start[0]); -+ CHECK (regs[0].end[0] != regs[2].end[0]); -+ CHECK (regs[1].start[0] != regs[3].start[0]); -+ CHECK (regs[1].end[0] != regs[3].end[0]); -+ } -+ -+ printf ("INFO: Checking C.UTF-8.\n"); -+ setlocale (LC_ALL, "C.UTF-8"); -+ -+ (void) re_set_syntax (RE_SYNTAX_GNU_AWK); - -- result = run_test (lower, regs); -+ result |= run_test (lower, regs); - result |= run_test (upper, ®s[2]); - if (! result) - { -diff --git a/posix/tst-fnmatch.input b/posix/tst-fnmatch.input -index 67aac5aadafd8aeb..6ff5318032e0afb2 100644 ---- a/posix/tst-fnmatch.input -+++ b/posix/tst-fnmatch.input -@@ -472,6 +472,397 @@ C "\\" "[Z-\\]]" 0 - C "]" "[Z-\\]]" 0 - C "-" "[Z-\\]]" NOMATCH - -+# B.6 004(C) -+C.UTF-8 "!#%+,-./01234567889" "!#%+,-./01234567889" 0 -+C.UTF-8 ":;=@ABCDEFGHIJKLMNO" ":;=@ABCDEFGHIJKLMNO" 0 -+C.UTF-8 "PQRSTUVWXYZ]abcdefg" "PQRSTUVWXYZ]abcdefg" 0 -+C.UTF-8 "hijklmnopqrstuvwxyz" "hijklmnopqrstuvwxyz" 0 -+C.UTF-8 "^_{}~" "^_{}~" 0 -+ -+# B.6 005(C) -+C.UTF-8 "\"$&'()" "\\\"\\$\\&\\'\\(\\)" 0 -+C.UTF-8 "*?[\\`|" "\\*\\?\\[\\\\\\`\\|" 0 -+C.UTF-8 "<>" "\\<\\>" 0 -+ -+# B.6 006(C) -+C.UTF-8 "?*[" "[?*[][?*[][?*[]" 0 -+C.UTF-8 "a/b" "?/b" 0 -+ -+# B.6 007(C) -+C.UTF-8 "a/b" "a?b" 0 -+C.UTF-8 "a/b" "a/?" 0 -+C.UTF-8 "aa/b" "?/b" NOMATCH -+C.UTF-8 "aa/b" "a?b" NOMATCH -+C.UTF-8 "a/bb" "a/?" NOMATCH -+ -+# B.6 009(C) -+C.UTF-8 "abc" "[abc]" NOMATCH -+C.UTF-8 "x" "[abc]" NOMATCH -+C.UTF-8 "a" "[abc]" 0 -+C.UTF-8 "[" "[[abc]" 0 -+C.UTF-8 "a" "[][abc]" 0 -+C.UTF-8 "a]" "[]a]]" 0 -+ -+# B.6 010(C) -+C.UTF-8 "xyz" "[!abc]" NOMATCH -+C.UTF-8 "x" "[!abc]" 0 -+C.UTF-8 "a" "[!abc]" NOMATCH -+ -+# B.6 011(C) -+C.UTF-8 "]" "[][abc]" 0 -+C.UTF-8 "abc]" "[][abc]" NOMATCH -+C.UTF-8 "[]abc" "[][]abc" NOMATCH -+C.UTF-8 "]" "[!]]" NOMATCH -+C.UTF-8 "aa]" "[!]a]" NOMATCH -+C.UTF-8 "]" "[!a]" 0 -+C.UTF-8 "]]" "[!a]]" 0 -+ -+# B.6 012(C) -+C.UTF-8 "a" "[[.a.]]" 0 -+C.UTF-8 "-" "[[.-.]]" 0 -+C.UTF-8 "-" "[[.-.][.].]]" 0 -+C.UTF-8 "-" "[[.].][.-.]]" 0 -+C.UTF-8 "-" "[[.-.][=u=]]" 0 -+C.UTF-8 "-" "[[.-.][:alpha:]]" 0 -+C.UTF-8 "a" "[![.a.]]" NOMATCH -+ -+# B.6 013(C) -+C.UTF-8 "a" "[[.b.]]" NOMATCH -+C.UTF-8 "a" "[[.b.][.c.]]" NOMATCH -+C.UTF-8 "a" "[[.b.][=b=]]" NOMATCH -+ -+ -+# B.6 015(C) -+C.UTF-8 "a" "[[=a=]]" 0 -+C.UTF-8 "b" "[[=a=]b]" 0 -+C.UTF-8 "b" "[[=a=][=b=]]" 0 -+C.UTF-8 "a" "[[=a=][=b=]]" 0 -+C.UTF-8 "a" "[[=a=][.b.]]" 0 -+C.UTF-8 "a" "[[=a=][:digit:]]" 0 -+ -+# B.6 016(C) -+C.UTF-8 "=" "[[=a=]b]" NOMATCH -+C.UTF-8 "]" "[[=a=]b]" NOMATCH -+C.UTF-8 "a" "[[=b=][=c=]]" NOMATCH -+C.UTF-8 "a" "[[=b=][.].]]" NOMATCH -+C.UTF-8 "a" "[[=b=][:digit:]]" NOMATCH -+ -+# B.6 017(C) -+C.UTF-8 "a" "[[:alnum:]]" 0 -+C.UTF-8 "a" "[![:alnum:]]" NOMATCH -+C.UTF-8 "-" "[[:alnum:]]" NOMATCH -+C.UTF-8 "a]a" "[[:alnum:]]a" NOMATCH -+C.UTF-8 "-" "[[:alnum:]-]" 0 -+C.UTF-8 "aa" "[[:alnum:]]a" 0 -+C.UTF-8 "-" "[![:alnum:]]" 0 -+C.UTF-8 "]" "[!][:alnum:]]" NOMATCH -+C.UTF-8 "[" "[![:alnum:][]" NOMATCH -+C.UTF-8 "a" "[[:alnum:]]" 0 -+C.UTF-8 "b" "[[:alnum:]]" 0 -+C.UTF-8 "c" "[[:alnum:]]" 0 -+C.UTF-8 "d" "[[:alnum:]]" 0 -+C.UTF-8 "e" "[[:alnum:]]" 0 -+C.UTF-8 "f" "[[:alnum:]]" 0 -+C.UTF-8 "g" "[[:alnum:]]" 0 -+C.UTF-8 "h" "[[:alnum:]]" 0 -+C.UTF-8 "i" "[[:alnum:]]" 0 -+C.UTF-8 "j" "[[:alnum:]]" 0 -+C.UTF-8 "k" "[[:alnum:]]" 0 -+C.UTF-8 "l" "[[:alnum:]]" 0 -+C.UTF-8 "m" "[[:alnum:]]" 0 -+C.UTF-8 "n" "[[:alnum:]]" 0 -+C.UTF-8 "o" "[[:alnum:]]" 0 -+C.UTF-8 "p" "[[:alnum:]]" 0 -+C.UTF-8 "q" "[[:alnum:]]" 0 -+C.UTF-8 "r" "[[:alnum:]]" 0 -+C.UTF-8 "s" "[[:alnum:]]" 0 -+C.UTF-8 "t" "[[:alnum:]]" 0 -+C.UTF-8 "u" "[[:alnum:]]" 0 -+C.UTF-8 "v" "[[:alnum:]]" 0 -+C.UTF-8 "w" "[[:alnum:]]" 0 -+C.UTF-8 "x" "[[:alnum:]]" 0 -+C.UTF-8 "y" "[[:alnum:]]" 0 -+C.UTF-8 "z" "[[:alnum:]]" 0 -+C.UTF-8 "A" "[[:alnum:]]" 0 -+C.UTF-8 "B" "[[:alnum:]]" 0 -+C.UTF-8 "C" "[[:alnum:]]" 0 -+C.UTF-8 "D" "[[:alnum:]]" 0 -+C.UTF-8 "E" "[[:alnum:]]" 0 -+C.UTF-8 "F" "[[:alnum:]]" 0 -+C.UTF-8 "G" "[[:alnum:]]" 0 -+C.UTF-8 "H" "[[:alnum:]]" 0 -+C.UTF-8 "I" "[[:alnum:]]" 0 -+C.UTF-8 "J" "[[:alnum:]]" 0 -+C.UTF-8 "K" "[[:alnum:]]" 0 -+C.UTF-8 "L" "[[:alnum:]]" 0 -+C.UTF-8 "M" "[[:alnum:]]" 0 -+C.UTF-8 "N" "[[:alnum:]]" 0 -+C.UTF-8 "O" "[[:alnum:]]" 0 -+C.UTF-8 "P" "[[:alnum:]]" 0 -+C.UTF-8 "Q" "[[:alnum:]]" 0 -+C.UTF-8 "R" "[[:alnum:]]" 0 -+C.UTF-8 "S" "[[:alnum:]]" 0 -+C.UTF-8 "T" "[[:alnum:]]" 0 -+C.UTF-8 "U" "[[:alnum:]]" 0 -+C.UTF-8 "V" "[[:alnum:]]" 0 -+C.UTF-8 "W" "[[:alnum:]]" 0 -+C.UTF-8 "X" "[[:alnum:]]" 0 -+C.UTF-8 "Y" "[[:alnum:]]" 0 -+C.UTF-8 "Z" "[[:alnum:]]" 0 -+C.UTF-8 "0" "[[:alnum:]]" 0 -+C.UTF-8 "1" "[[:alnum:]]" 0 -+C.UTF-8 "2" "[[:alnum:]]" 0 -+C.UTF-8 "3" "[[:alnum:]]" 0 -+C.UTF-8 "4" "[[:alnum:]]" 0 -+C.UTF-8 "5" "[[:alnum:]]" 0 -+C.UTF-8 "6" "[[:alnum:]]" 0 -+C.UTF-8 "7" "[[:alnum:]]" 0 -+C.UTF-8 "8" "[[:alnum:]]" 0 -+C.UTF-8 "9" "[[:alnum:]]" 0 -+C.UTF-8 "!" "[[:alnum:]]" NOMATCH -+C.UTF-8 "#" "[[:alnum:]]" NOMATCH -+C.UTF-8 "%" "[[:alnum:]]" NOMATCH -+C.UTF-8 "+" "[[:alnum:]]" NOMATCH -+C.UTF-8 "," "[[:alnum:]]" NOMATCH -+C.UTF-8 "-" "[[:alnum:]]" NOMATCH -+C.UTF-8 "." "[[:alnum:]]" NOMATCH -+C.UTF-8 "/" "[[:alnum:]]" NOMATCH -+C.UTF-8 ":" "[[:alnum:]]" NOMATCH -+C.UTF-8 ";" "[[:alnum:]]" NOMATCH -+C.UTF-8 "=" "[[:alnum:]]" NOMATCH -+C.UTF-8 "@" "[[:alnum:]]" NOMATCH -+C.UTF-8 "[" "[[:alnum:]]" NOMATCH -+C.UTF-8 "\\" "[[:alnum:]]" NOMATCH -+C.UTF-8 "]" "[[:alnum:]]" NOMATCH -+C.UTF-8 "^" "[[:alnum:]]" NOMATCH -+C.UTF-8 "_" "[[:alnum:]]" NOMATCH -+C.UTF-8 "{" "[[:alnum:]]" NOMATCH -+C.UTF-8 "}" "[[:alnum:]]" NOMATCH -+C.UTF-8 "~" "[[:alnum:]]" NOMATCH -+C.UTF-8 "\"" "[[:alnum:]]" NOMATCH -+C.UTF-8 "$" "[[:alnum:]]" NOMATCH -+C.UTF-8 "&" "[[:alnum:]]" NOMATCH -+C.UTF-8 "'" "[[:alnum:]]" NOMATCH -+C.UTF-8 "(" "[[:alnum:]]" NOMATCH -+C.UTF-8 ")" "[[:alnum:]]" NOMATCH -+C.UTF-8 "*" "[[:alnum:]]" NOMATCH -+C.UTF-8 "?" "[[:alnum:]]" NOMATCH -+C.UTF-8 "`" "[[:alnum:]]" NOMATCH -+C.UTF-8 "|" "[[:alnum:]]" NOMATCH -+C.UTF-8 "<" "[[:alnum:]]" NOMATCH -+C.UTF-8 ">" "[[:alnum:]]" NOMATCH -+C.UTF-8 "\t" "[[:cntrl:]]" 0 -+C.UTF-8 "t" "[[:cntrl:]]" NOMATCH -+C.UTF-8 "t" "[[:lower:]]" 0 -+C.UTF-8 "\t" "[[:lower:]]" NOMATCH -+C.UTF-8 "T" "[[:lower:]]" NOMATCH -+C.UTF-8 "\t" "[[:space:]]" 0 -+C.UTF-8 "t" "[[:space:]]" NOMATCH -+C.UTF-8 "t" "[[:alpha:]]" 0 -+C.UTF-8 "\t" "[[:alpha:]]" NOMATCH -+C.UTF-8 "0" "[[:digit:]]" 0 -+C.UTF-8 "\t" "[[:digit:]]" NOMATCH -+C.UTF-8 "t" "[[:digit:]]" NOMATCH -+C.UTF-8 "\t" "[[:print:]]" NOMATCH -+C.UTF-8 "t" "[[:print:]]" 0 -+C.UTF-8 "T" "[[:upper:]]" 0 -+C.UTF-8 "\t" "[[:upper:]]" NOMATCH -+C.UTF-8 "t" "[[:upper:]]" NOMATCH -+C.UTF-8 "\t" "[[:blank:]]" 0 -+C.UTF-8 "t" "[[:blank:]]" NOMATCH -+C.UTF-8 "\t" "[[:graph:]]" NOMATCH -+C.UTF-8 "t" "[[:graph:]]" 0 -+C.UTF-8 "." "[[:punct:]]" 0 -+C.UTF-8 "t" "[[:punct:]]" NOMATCH -+C.UTF-8 "\t" "[[:punct:]]" NOMATCH -+C.UTF-8 "0" "[[:xdigit:]]" 0 -+C.UTF-8 "\t" "[[:xdigit:]]" NOMATCH -+C.UTF-8 "a" "[[:xdigit:]]" 0 -+C.UTF-8 "A" "[[:xdigit:]]" 0 -+C.UTF-8 "t" "[[:xdigit:]]" NOMATCH -+C.UTF-8 "a" "[[alpha]]" NOMATCH -+C.UTF-8 "a" "[[alpha:]]" NOMATCH -+C.UTF-8 "a]" "[[alpha]]" 0 -+C.UTF-8 "a]" "[[alpha:]]" 0 -+C.UTF-8 "a" "[[:alpha:][.b.]]" 0 -+C.UTF-8 "a" "[[:alpha:][=b=]]" 0 -+C.UTF-8 "a" "[[:alpha:][:digit:]]" 0 -+C.UTF-8 "a" "[[:digit:][:alpha:]]" 0 -+ -+# B.6 018(C) -+C.UTF-8 "a" "[a-c]" 0 -+C.UTF-8 "b" "[a-c]" 0 -+C.UTF-8 "c" "[a-c]" 0 -+C.UTF-8 "a" "[b-c]" NOMATCH -+C.UTF-8 "d" "[b-c]" NOMATCH -+C.UTF-8 "B" "[a-c]" NOMATCH -+C.UTF-8 "b" "[A-C]" NOMATCH -+C.UTF-8 "" "[a-c]" NOMATCH -+C.UTF-8 "as" "[a-ca-z]" NOMATCH -+C.UTF-8 "a" "[[.a.]-c]" 0 -+C.UTF-8 "a" "[a-[.c.]]" 0 -+C.UTF-8 "a" "[[.a.]-[.c.]]" 0 -+C.UTF-8 "b" "[[.a.]-c]" 0 -+C.UTF-8 "b" "[a-[.c.]]" 0 -+C.UTF-8 "b" "[[.a.]-[.c.]]" 0 -+C.UTF-8 "c" "[[.a.]-c]" 0 -+C.UTF-8 "c" "[a-[.c.]]" 0 -+C.UTF-8 "c" "[[.a.]-[.c.]]" 0 -+C.UTF-8 "d" "[[.a.]-c]" NOMATCH -+C.UTF-8 "d" "[a-[.c.]]" NOMATCH -+C.UTF-8 "d" "[[.a.]-[.c.]]" NOMATCH -+ -+# B.6 019(C) -+C.UTF-8 "a" "[c-a]" NOMATCH -+C.UTF-8 "a" "[[.c.]-a]" NOMATCH -+C.UTF-8 "a" "[c-[.a.]]" NOMATCH -+C.UTF-8 "a" "[[.c.]-[.a.]]" NOMATCH -+C.UTF-8 "c" "[c-a]" NOMATCH -+C.UTF-8 "c" "[[.c.]-a]" NOMATCH -+C.UTF-8 "c" "[c-[.a.]]" NOMATCH -+C.UTF-8 "c" "[[.c.]-[.a.]]" NOMATCH -+ -+# B.6 020(C) -+C.UTF-8 "a" "[a-c0-9]" 0 -+C.UTF-8 "d" "[a-c0-9]" NOMATCH -+C.UTF-8 "B" "[a-c0-9]" NOMATCH -+ -+# B.6 021(C) -+C.UTF-8 "-" "[-a]" 0 -+C.UTF-8 "a" "[-b]" NOMATCH -+C.UTF-8 "-" "[!-a]" NOMATCH -+C.UTF-8 "a" "[!-b]" 0 -+C.UTF-8 "-" "[a-c-0-9]" 0 -+C.UTF-8 "b" "[a-c-0-9]" 0 -+C.UTF-8 "a:" "a[0-9-a]" NOMATCH -+C.UTF-8 "a:" "a[09-a]" 0 -+ -+# B.6 024(C) -+C.UTF-8 "" "*" 0 -+C.UTF-8 "asd/sdf" "*" 0 -+ -+# B.6 025(C) -+C.UTF-8 "as" "[a-c][a-z]" 0 -+C.UTF-8 "as" "??" 0 -+ -+# B.6 026(C) -+C.UTF-8 "asd/sdf" "as*df" 0 -+C.UTF-8 "asd/sdf" "as*" 0 -+C.UTF-8 "asd/sdf" "*df" 0 -+C.UTF-8 "asd/sdf" "as*dg" NOMATCH -+C.UTF-8 "asdf" "as*df" 0 -+C.UTF-8 "asdf" "as*df?" NOMATCH -+C.UTF-8 "asdf" "as*??" 0 -+C.UTF-8 "asdf" "a*???" 0 -+C.UTF-8 "asdf" "*????" 0 -+C.UTF-8 "asdf" "????*" 0 -+C.UTF-8 "asdf" "??*?" 0 -+ -+# B.6 027(C) -+C.UTF-8 "/" "/" 0 -+C.UTF-8 "/" "/*" 0 -+C.UTF-8 "/" "*/" 0 -+C.UTF-8 "/" "/?" NOMATCH -+C.UTF-8 "/" "?/" NOMATCH -+C.UTF-8 "/" "?" 0 -+C.UTF-8 "." "?" 0 -+C.UTF-8 "/." "??" 0 -+C.UTF-8 "/" "[!a-c]" 0 -+C.UTF-8 "." "[!a-c]" 0 -+ -+# B.6 029(C) -+C.UTF-8 "/" "/" 0 PATHNAME -+C.UTF-8 "//" "//" 0 PATHNAME -+C.UTF-8 "/.a" "/*" 0 PATHNAME -+C.UTF-8 "/.a" "/?a" 0 PATHNAME -+C.UTF-8 "/.a" "/[!a-z]a" 0 PATHNAME -+C.UTF-8 "/.a/.b" "/*/?b" 0 PATHNAME -+ -+# B.6 030(C) -+C.UTF-8 "/" "?" NOMATCH PATHNAME -+C.UTF-8 "/" "*" NOMATCH PATHNAME -+C.UTF-8 "a/b" "a?b" NOMATCH PATHNAME -+C.UTF-8 "/.a/.b" "/*b" NOMATCH PATHNAME -+ -+# B.6 031(C) -+C.UTF-8 "/$" "\\/\\$" 0 -+C.UTF-8 "/[" "\\/\\[" 0 -+C.UTF-8 "/[" "\\/[" 0 -+C.UTF-8 "/[]" "\\/\\[]" 0 -+ -+# B.6 032(C) -+C.UTF-8 "/$" "\\/\\$" NOMATCH NOESCAPE -+C.UTF-8 "/\\$" "\\/\\$" NOMATCH NOESCAPE -+C.UTF-8 "\\/\\$" "\\/\\$" 0 NOESCAPE -+ -+# B.6 033(C) -+C.UTF-8 ".asd" ".*" 0 PERIOD -+C.UTF-8 "/.asd" "*" 0 PERIOD -+C.UTF-8 "/as/.df" "*/?*f" 0 PERIOD -+C.UTF-8 "..asd" ".[!a-z]*" 0 PERIOD -+ -+# B.6 034(C) -+C.UTF-8 ".asd" "*" NOMATCH PERIOD -+C.UTF-8 ".asd" "?asd" NOMATCH PERIOD -+C.UTF-8 ".asd" "[!a-z]*" NOMATCH PERIOD -+ -+# B.6 035(C) -+C.UTF-8 "/." "/." 0 PATHNAME|PERIOD -+C.UTF-8 "/.a./.b." "/.*/.*" 0 PATHNAME|PERIOD -+C.UTF-8 "/.a./.b." "/.??/.??" 0 PATHNAME|PERIOD -+ -+# B.6 036(C) -+C.UTF-8 "/." "*" NOMATCH PATHNAME|PERIOD -+C.UTF-8 "/." "/*" NOMATCH PATHNAME|PERIOD -+C.UTF-8 "/." "/?" NOMATCH PATHNAME|PERIOD -+C.UTF-8 "/." "/[!a-z]" NOMATCH PATHNAME|PERIOD -+C.UTF-8 "/a./.b." "/*/*" NOMATCH PATHNAME|PERIOD -+C.UTF-8 "/a./.b." "/??/???" NOMATCH PATHNAME|PERIOD -+ -+# Some home-grown tests. -+C.UTF-8 "foobar" "foo*[abc]z" NOMATCH -+C.UTF-8 "foobaz" "foo*[abc][xyz]" 0 -+C.UTF-8 "foobaz" "foo?*[abc][xyz]" 0 -+C.UTF-8 "foobaz" "foo?*[abc][x/yz]" 0 -+C.UTF-8 "foobaz" "foo?*[abc]/[xyz]" NOMATCH PATHNAME -+C.UTF-8 "a" "a/" NOMATCH PATHNAME -+C.UTF-8 "a/" "a" NOMATCH PATHNAME -+C.UTF-8 "//a" "/a" NOMATCH PATHNAME -+C.UTF-8 "/a" "//a" NOMATCH PATHNAME -+C.UTF-8 "az" "[a-]z" 0 -+C.UTF-8 "bz" "[ab-]z" 0 -+C.UTF-8 "cz" "[ab-]z" NOMATCH -+C.UTF-8 "-z" "[ab-]z" 0 -+C.UTF-8 "az" "[-a]z" 0 -+C.UTF-8 "bz" "[-ab]z" 0 -+C.UTF-8 "cz" "[-ab]z" NOMATCH -+C.UTF-8 "-z" "[-ab]z" 0 -+C.UTF-8 "\\" "[\\\\-a]" 0 -+C.UTF-8 "_" "[\\\\-a]" 0 -+C.UTF-8 "a" "[\\\\-a]" 0 -+C.UTF-8 "-" "[\\\\-a]" NOMATCH -+C.UTF-8 "\\" "[\\]-a]" NOMATCH -+C.UTF-8 "_" "[\\]-a]" 0 -+C.UTF-8 "a" "[\\]-a]" 0 -+C.UTF-8 "]" "[\\]-a]" 0 -+C.UTF-8 "-" "[\\]-a]" NOMATCH -+C.UTF-8 "\\" "[!\\\\-a]" NOMATCH -+C.UTF-8 "_" "[!\\\\-a]" NOMATCH -+C.UTF-8 "a" "[!\\\\-a]" NOMATCH -+C.UTF-8 "-" "[!\\\\-a]" 0 -+C.UTF-8 "!" "[\\!-]" 0 -+C.UTF-8 "-" "[\\!-]" 0 -+C.UTF-8 "\\" "[\\!-]" NOMATCH -+C.UTF-8 "Z" "[Z-\\\\]" 0 -+C.UTF-8 "[" "[Z-\\\\]" 0 -+C.UTF-8 "\\" "[Z-\\\\]" 0 -+C.UTF-8 "-" "[Z-\\\\]" NOMATCH -+C.UTF-8 "Z" "[Z-\\]]" 0 -+C.UTF-8 "[" "[Z-\\]]" 0 -+C.UTF-8 "\\" "[Z-\\]]" 0 -+C.UTF-8 "]" "[Z-\\]]" 0 -+C.UTF-8 "-" "[Z-\\]]" NOMATCH -+ - # Following are tests outside the scope of IEEE 2003.2 since they are using - # locales other than the C locale. The main focus of the tests is on the - # handling of ranges and the recognition of character (vs bytes). -@@ -677,7 +1068,6 @@ C "x/y" "*" 0 PATHNAME|LEADING_DIR - C "x/y/z" "*" 0 PATHNAME|LEADING_DIR - C "x" "*x" 0 PATHNAME|LEADING_DIR - --en_US.UTF-8 "\366.csv" "*.csv" 0 - C "x/y" "*x" 0 PATHNAME|LEADING_DIR - C "x/y/z" "*x" 0 PATHNAME|LEADING_DIR - C "x" "x*" 0 PATHNAME|LEADING_DIR -@@ -693,6 +1083,33 @@ C "x" "x?y" NOMATCH PATHNAME|LEADING_DIR - C "x/y" "x?y" NOMATCH PATHNAME|LEADING_DIR - C "x/y/z" "x?y" NOMATCH PATHNAME|LEADING_DIR - -+# Duplicate the "Test of GNU extensions." tests but for C.UTF-8. -+C.UTF-8 "x" "x" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y" "x" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "x" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x" "*" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y" "*" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "*" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x" "*x" 0 PATHNAME|LEADING_DIR -+ -+C.UTF-8 "x/y" "*x" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "*x" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x" "x*" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y" "x*" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "x*" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x" "a" NOMATCH PATHNAME|LEADING_DIR -+C.UTF-8 "x/y" "a" NOMATCH PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "a" NOMATCH PATHNAME|LEADING_DIR -+C.UTF-8 "x" "x/y" NOMATCH PATHNAME|LEADING_DIR -+C.UTF-8 "x/y" "x/y" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "x/y" 0 PATHNAME|LEADING_DIR -+C.UTF-8 "x" "x?y" NOMATCH PATHNAME|LEADING_DIR -+C.UTF-8 "x/y" "x?y" NOMATCH PATHNAME|LEADING_DIR -+C.UTF-8 "x/y/z" "x?y" NOMATCH PATHNAME|LEADING_DIR -+ -+# Bug 14185 -+en_US.UTF-8 "\366.csv" "*.csv" 0 -+ - # ksh style matching. - C "abcd" "?@(a|b)*@(c)d" 0 EXTMATCH - C "/dev/udp/129.22.8.102/45" "/dev/@(tcp|udp)/*/*" 0 PATHNAME|EXTMATCH -@@ -822,3 +1239,133 @@ C "" "" 0 - C "" "" 0 EXTMATCH - C "" "*([abc])" 0 EXTMATCH - C "" "?([abc])" 0 EXTMATCH -+ -+# Duplicate the "ksh style matching." for C.UTF-8. -+C.UTF-8 "abcd" "?@(a|b)*@(c)d" 0 EXTMATCH -+C.UTF-8 "/dev/udp/129.22.8.102/45" "/dev/@(tcp|udp)/*/*" 0 PATHNAME|EXTMATCH -+C.UTF-8 "12" "[1-9]*([0-9])" 0 EXTMATCH -+C.UTF-8 "12abc" "[1-9]*([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "1" "[1-9]*([0-9])" 0 EXTMATCH -+C.UTF-8 "07" "+([0-7])" 0 EXTMATCH -+C.UTF-8 "0377" "+([0-7])" 0 EXTMATCH -+C.UTF-8 "09" "+([0-7])" NOMATCH EXTMATCH -+C.UTF-8 "paragraph" "para@(chute|graph)" 0 EXTMATCH -+C.UTF-8 "paramour" "para@(chute|graph)" NOMATCH EXTMATCH -+C.UTF-8 "para991" "para?([345]|99)1" 0 EXTMATCH -+C.UTF-8 "para381" "para?([345]|99)1" NOMATCH EXTMATCH -+C.UTF-8 "paragraph" "para*([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "para" "para*([0-9])" 0 EXTMATCH -+C.UTF-8 "para13829383746592" "para*([0-9])" 0 EXTMATCH -+C.UTF-8 "paragraph" "para+([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "para" "para+([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "para987346523" "para+([0-9])" 0 EXTMATCH -+C.UTF-8 "paragraph" "para!(*.[0-9])" 0 EXTMATCH -+C.UTF-8 "para.38" "para!(*.[0-9])" 0 EXTMATCH -+C.UTF-8 "para.graph" "para!(*.[0-9])" 0 EXTMATCH -+C.UTF-8 "para39" "para!(*.[0-9])" 0 EXTMATCH -+C.UTF-8 "" "*(0|1|3|5|7|9)" 0 EXTMATCH -+C.UTF-8 "137577991" "*(0|1|3|5|7|9)" 0 EXTMATCH -+C.UTF-8 "2468" "*(0|1|3|5|7|9)" NOMATCH EXTMATCH -+C.UTF-8 "1358" "*(0|1|3|5|7|9)" NOMATCH EXTMATCH -+C.UTF-8 "file.c" "*.c?(c)" 0 EXTMATCH -+C.UTF-8 "file.C" "*.c?(c)" NOMATCH EXTMATCH -+C.UTF-8 "file.cc" "*.c?(c)" 0 EXTMATCH -+C.UTF-8 "file.ccc" "*.c?(c)" NOMATCH EXTMATCH -+C.UTF-8 "parse.y" "!(*.c|*.h|Makefile.in|config*|README)" 0 EXTMATCH -+C.UTF-8 "shell.c" "!(*.c|*.h|Makefile.in|config*|README)" NOMATCH EXTMATCH -+C.UTF-8 "Makefile" "!(*.c|*.h|Makefile.in|config*|README)" 0 EXTMATCH -+C.UTF-8 "VMS.FILE;1" "*\;[1-9]*([0-9])" 0 EXTMATCH -+C.UTF-8 "VMS.FILE;0" "*\;[1-9]*([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "VMS.FILE;" "*\;[1-9]*([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "VMS.FILE;139" "*\;[1-9]*([0-9])" 0 EXTMATCH -+C.UTF-8 "VMS.FILE;1N" "*\;[1-9]*([0-9])" NOMATCH EXTMATCH -+C.UTF-8 "abcfefg" "ab**(e|f)" 0 EXTMATCH -+C.UTF-8 "abcfefg" "ab**(e|f)g" 0 EXTMATCH -+C.UTF-8 "ab" "ab*+(e|f)" NOMATCH EXTMATCH -+C.UTF-8 "abef" "ab***ef" 0 EXTMATCH -+C.UTF-8 "abef" "ab**" 0 EXTMATCH -+C.UTF-8 "fofo" "*(f*(o))" 0 EXTMATCH -+C.UTF-8 "ffo" "*(f*(o))" 0 EXTMATCH -+C.UTF-8 "foooofo" "*(f*(o))" 0 EXTMATCH -+C.UTF-8 "foooofof" "*(f*(o))" 0 EXTMATCH -+C.UTF-8 "fooofoofofooo" "*(f*(o))" 0 EXTMATCH -+C.UTF-8 "foooofof" "*(f+(o))" NOMATCH EXTMATCH -+C.UTF-8 "xfoooofof" "*(f*(o))" NOMATCH EXTMATCH -+C.UTF-8 "foooofofx" "*(f*(o))" NOMATCH EXTMATCH -+C.UTF-8 "ofxoofxo" "*(*(of*(o)x)o)" 0 EXTMATCH -+C.UTF-8 "ofooofoofofooo" "*(f*(o))" NOMATCH EXTMATCH -+C.UTF-8 "foooxfooxfoxfooox" "*(f*(o)x)" 0 EXTMATCH -+C.UTF-8 "foooxfooxofoxfooox" "*(f*(o)x)" NOMATCH EXTMATCH -+C.UTF-8 "foooxfooxfxfooox" "*(f*(o)x)" 0 EXTMATCH -+C.UTF-8 "ofxoofxo" "*(*(of*(o)x)o)" 0 EXTMATCH -+C.UTF-8 "ofoooxoofxo" "*(*(of*(o)x)o)" 0 EXTMATCH -+C.UTF-8 "ofoooxoofxoofoooxoofxo" "*(*(of*(o)x)o)" 0 EXTMATCH -+C.UTF-8 "ofoooxoofxoofoooxoofxoo" "*(*(of*(o)x)o)" 0 EXTMATCH -+C.UTF-8 "ofoooxoofxoofoooxoofxofo" "*(*(of*(o)x)o)" NOMATCH EXTMATCH -+C.UTF-8 "ofoooxoofxoofoooxoofxooofxofxo" "*(*(of*(o)x)o)" 0 EXTMATCH -+C.UTF-8 "aac" "*(@(a))a@(c)" 0 EXTMATCH -+C.UTF-8 "ac" "*(@(a))a@(c)" 0 EXTMATCH -+C.UTF-8 "c" "*(@(a))a@(c)" NOMATCH EXTMATCH -+C.UTF-8 "aaac" "*(@(a))a@(c)" 0 EXTMATCH -+C.UTF-8 "baaac" "*(@(a))a@(c)" NOMATCH EXTMATCH -+C.UTF-8 "abcd" "?@(a|b)*@(c)d" 0 EXTMATCH -+C.UTF-8 "abcd" "@(ab|a*@(b))*(c)d" 0 EXTMATCH -+C.UTF-8 "acd" "@(ab|a*(b))*(c)d" 0 EXTMATCH -+C.UTF-8 "abbcd" "@(ab|a*(b))*(c)d" 0 EXTMATCH -+C.UTF-8 "effgz" "@(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH -+C.UTF-8 "efgz" "@(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH -+C.UTF-8 "egz" "@(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH -+C.UTF-8 "egzefffgzbcdij" "*(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH -+C.UTF-8 "egz" "@(b+(c)d|e+(f)g?|?(h)i@(j|k))" NOMATCH EXTMATCH -+C.UTF-8 "ofoofo" "*(of+(o))" 0 EXTMATCH -+C.UTF-8 "oxfoxoxfox" "*(oxf+(ox))" 0 EXTMATCH -+C.UTF-8 "oxfoxfox" "*(oxf+(ox))" NOMATCH EXTMATCH -+C.UTF-8 "ofoofo" "*(of+(o)|f)" 0 EXTMATCH -+C.UTF-8 "foofoofo" "@(foo|f|fo)*(f|of+(o))" 0 EXTMATCH -+C.UTF-8 "oofooofo" "*(of|oof+(o))" 0 EXTMATCH -+C.UTF-8 "fffooofoooooffoofffooofff" "*(*(f)*(o))" 0 EXTMATCH -+C.UTF-8 "fofoofoofofoo" "*(fo|foo)" 0 EXTMATCH -+C.UTF-8 "foo" "!(x)" 0 EXTMATCH -+C.UTF-8 "foo" "!(x)*" 0 EXTMATCH -+C.UTF-8 "foo" "!(foo)" NOMATCH EXTMATCH -+C.UTF-8 "foo" "!(foo)*" 0 EXTMATCH -+C.UTF-8 "foobar" "!(foo)" 0 EXTMATCH -+C.UTF-8 "foobar" "!(foo)*" 0 EXTMATCH -+C.UTF-8 "moo.cow" "!(*.*).!(*.*)" 0 EXTMATCH -+C.UTF-8 "mad.moo.cow" "!(*.*).!(*.*)" NOMATCH EXTMATCH -+C.UTF-8 "mucca.pazza" "mu!(*(c))?.pa!(*(z))?" NOMATCH EXTMATCH -+C.UTF-8 "fff" "!(f)" 0 EXTMATCH -+C.UTF-8 "fff" "*(!(f))" 0 EXTMATCH -+C.UTF-8 "fff" "+(!(f))" 0 EXTMATCH -+C.UTF-8 "ooo" "!(f)" 0 EXTMATCH -+C.UTF-8 "ooo" "*(!(f))" 0 EXTMATCH -+C.UTF-8 "ooo" "+(!(f))" 0 EXTMATCH -+C.UTF-8 "foo" "!(f)" 0 EXTMATCH -+C.UTF-8 "foo" "*(!(f))" 0 EXTMATCH -+C.UTF-8 "foo" "+(!(f))" 0 EXTMATCH -+C.UTF-8 "f" "!(f)" NOMATCH EXTMATCH -+C.UTF-8 "f" "*(!(f))" NOMATCH EXTMATCH -+C.UTF-8 "f" "+(!(f))" NOMATCH EXTMATCH -+C.UTF-8 "foot" "@(!(z*)|*x)" 0 EXTMATCH -+C.UTF-8 "zoot" "@(!(z*)|*x)" NOMATCH EXTMATCH -+C.UTF-8 "foox" "@(!(z*)|*x)" 0 EXTMATCH -+C.UTF-8 "zoox" "@(!(z*)|*x)" 0 EXTMATCH -+C.UTF-8 "foo" "*(!(foo))" 0 EXTMATCH -+C.UTF-8 "foob" "!(foo)b*" NOMATCH EXTMATCH -+C.UTF-8 "foobb" "!(foo)b*" 0 EXTMATCH -+C.UTF-8 "[" "*([a[])" 0 EXTMATCH -+C.UTF-8 "]" "*([]a[])" 0 EXTMATCH -+C.UTF-8 "a" "*([]a[])" 0 EXTMATCH -+C.UTF-8 "b" "*([!]a[])" 0 EXTMATCH -+C.UTF-8 "[" "*([!]a[]|[[])" 0 EXTMATCH -+C.UTF-8 "]" "*([!]a[]|[]])" 0 EXTMATCH -+C.UTF-8 "[" "!([!]a[])" 0 EXTMATCH -+C.UTF-8 "]" "!([!]a[])" 0 EXTMATCH -+C.UTF-8 ")" "*([)])" 0 EXTMATCH -+C.UTF-8 "*" "*([*(])" 0 EXTMATCH -+C.UTF-8 "abcd" "*!(|a)cd" 0 EXTMATCH -+C.UTF-8 "ab/.a" "+([abc])/*" NOMATCH EXTMATCH|PATHNAME|PERIOD -+C.UTF-8 "" "" 0 -+C.UTF-8 "" "" 0 EXTMATCH -+C.UTF-8 "" "*([abc])" 0 EXTMATCH -+C.UTF-8 "" "?([abc])" 0 EXTMATCH -diff --git a/posix/tst-regcomp-truncated.c b/posix/tst-regcomp-truncated.c -index 84195fcd2ec153b8..da3f97799e37c607 100644 ---- a/posix/tst-regcomp-truncated.c -+++ b/posix/tst-regcomp-truncated.c -@@ -37,6 +37,7 @@ - static const char locales[][17] = - { - "C", -+ "C.UTF-8", - "en_US.UTF-8", - "de_DE.ISO-8859-1", - }; -diff --git a/posix/tst-regex.c b/posix/tst-regex.c -index e7c2b05e8666a16e..531128de2a9176fa 100644 ---- a/posix/tst-regex.c -+++ b/posix/tst-regex.c -@@ -32,6 +32,7 @@ - #include - #include - #include -+#include - - - #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0 -@@ -58,7 +59,7 @@ do_test (void) - const char *file; - int fd; - struct stat st; -- int result; -+ int result = 0; - char *inmem; - char *outmem; - size_t inlen; -@@ -123,7 +124,7 @@ do_test (void) - - /* Run the actual tests. All tests are run in a single-byte and a - multi-byte locale. */ -- result = test_expr ("[äáàâéèêíìîñöóòôüúùû]", 4, 4); -+ result |= test_expr ("[äáàâéèêíìîñöóòôüúùû]", 4, 4); - result |= test_expr ("G.ran", 2, 3); - result |= test_expr ("G.\\{1\\}ran", 2, 3); - result |= test_expr ("G.*ran", 3, 44); -@@ -143,19 +144,33 @@ do_test (void) - static int - test_expr (const char *expr, int expected, int expectedicase) - { -- int result; -+ int result = 0; - char *inmem; - char *outmem; - size_t inlen; - size_t outlen; - char *uexpr; - -- /* First test: search with an UTF-8 locale. */ -- if (setlocale (LC_ALL, "de_DE.UTF-8") == NULL) -- error (EXIT_FAILURE, 0, "cannot set locale de_DE.UTF-8"); -+ /* First test: search with basic C.UTF-8 locale. */ -+ printf ("INFO: Testing C.UTF-8.\n"); -+ xsetlocale (LC_ALL, "C.UTF-8"); - - printf ("\nTest \"%s\" with multi-byte locale\n", expr); -- result = run_test (expr, mem, memlen, 0, expected); -+ result |= run_test (expr, mem, memlen, 0, expected); -+ printf ("\nTest \"%s\" with multi-byte locale, case insensitive\n", expr); -+ result |= run_test (expr, mem, memlen, 1, expectedicase); -+ printf ("\nTest \"%s\" backwards with multi-byte locale\n", expr); -+ result |= run_test_backwards (expr, mem, memlen, 0, expected); -+ printf ("\nTest \"%s\" backwards with multi-byte locale, case insensitive\n", -+ expr); -+ result |= run_test_backwards (expr, mem, memlen, 1, expectedicase); -+ -+ /* Second test: search with an UTF-8 locale. */ -+ printf ("INFO: Testing de_DE.UTF-8.\n"); -+ xsetlocale (LC_ALL, "de_DE.UTF-8"); -+ -+ printf ("\nTest \"%s\" with multi-byte locale\n", expr); -+ result |= run_test (expr, mem, memlen, 0, expected); - printf ("\nTest \"%s\" with multi-byte locale, case insensitive\n", expr); - result |= run_test (expr, mem, memlen, 1, expectedicase); - printf ("\nTest \"%s\" backwards with multi-byte locale\n", expr); -@@ -165,8 +180,8 @@ test_expr (const char *expr, int expected, int expectedicase) - result |= run_test_backwards (expr, mem, memlen, 1, expectedicase); - - /* Second test: search with an ISO-8859-1 locale. */ -- if (setlocale (LC_ALL, "de_DE.ISO-8859-1") == NULL) -- error (EXIT_FAILURE, 0, "cannot set locale de_DE.ISO-8859-1"); -+ printf ("INFO: Testing de_DE.ISO-8859-1.\n"); -+ xsetlocale (LC_ALL, "de_DE.ISO-8859-1"); - - inmem = (char *) expr; - inlen = strlen (expr); diff --git a/SOURCES/glibc-rh2234715.patch b/SOURCES/glibc-rh2234715.patch new file mode 100644 index 0000000..cdf298e --- /dev/null +++ b/SOURCES/glibc-rh2234715.patch @@ -0,0 +1,187 @@ +commit bd77dd7e73e3530203be1c52c8a29d08270cb25d +Author: Florian Weimer +Date: Wed Sep 13 14:10:56 2023 +0200 + + CVE-2023-4527: Stack read overflow with large TCP responses in no-aaaa mode + + Without passing alt_dns_packet_buffer, __res_context_search can only + store 2048 bytes (what fits into dns_packet_buffer). However, + the function returns the total packet size, and the subsequent + DNS parsing code in _nss_dns_gethostbyname4_r reads beyond the end + of the stack-allocated buffer. + + Fixes commit f282cdbe7f436c75864e5640a4 ("resolv: Implement no-aaaa + stub resolver option") and bug 30842. + +Conflits: + resolv/Makefile + (missing tests) + +diff --git a/resolv/Makefile b/resolv/Makefile +index ea1518ec2da860c1..2c43d52122ef4343 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -102,6 +102,7 @@ tests += \ + tst-resolv-invalid-cname \ + tst-resolv-network \ + tst-resolv-noaaaa \ ++ tst-resolv-noaaaa-vc \ + tst-resolv-nondecimal \ + tst-resolv-res_init-multi \ + tst-resolv-search \ +@@ -280,6 +281,7 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \ + $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \ + $(shared-thread-library) + $(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \ + $(shared-thread-library) + $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library) +diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c +index 36789965c06757d0..3d261b6810bba5c9 100644 +--- a/resolv/nss_dns/dns-host.c ++++ b/resolv/nss_dns/dns-host.c +@@ -428,7 +428,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat, + { + n = __res_context_search (ctx, name, C_IN, T_A, + dns_packet_buffer, sizeof (dns_packet_buffer), +- NULL, NULL, NULL, NULL, NULL); ++ &alt_dns_packet_buffer, NULL, NULL, NULL, NULL); + if (n >= 0) + status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n, + &abuf, pat, errnop, herrnop, ttlp); +diff --git a/resolv/tst-resolv-noaaaa-vc.c b/resolv/tst-resolv-noaaaa-vc.c +new file mode 100644 +index 0000000000000000..9f5aebd99f2d74a2 +--- /dev/null ++++ b/resolv/tst-resolv-noaaaa-vc.c +@@ -0,0 +1,129 @@ ++/* Test the RES_NOAAAA resolver option with a large response. ++ Copyright (C) 2022-2023 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 ++ ++/* Used to keep track of the number of queries. */ ++static volatile unsigned int queries; ++ ++/* If true, add a large TXT record at the start of the answer section. */ ++static volatile bool stuff_txt; ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ /* If not using TCP, just force its use. */ ++ if (!ctx->tcp) ++ { ++ struct resolv_response_flags flags = {.tc = true}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ return; ++ } ++ ++ /* The test needs to send four queries, the first three are used to ++ grow the NSS buffer via the ERANGE handshake. */ ++ ++queries; ++ TEST_VERIFY (queries <= 4); ++ ++ /* AAAA queries are supposed to be disabled. */ ++ TEST_COMPARE (qtype, T_A); ++ TEST_COMPARE (qclass, C_IN); ++ TEST_COMPARE_STRING (qname, "example.com"); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ ++ resolv_response_section (b, ns_s_an); ++ ++ if (stuff_txt) ++ { ++ resolv_response_open_record (b, qname, qclass, T_TXT, 60); ++ int zero = 0; ++ for (int i = 0; i <= 15000; ++i) ++ resolv_response_add_data (b, &zero, sizeof (zero)); ++ resolv_response_close_record (b); ++ } ++ ++ for (int i = 0; i < 200; ++i) ++ { ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ char ipv4[4] = {192, 0, 2, i + 1}; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ resolv_response_close_record (b); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ _res.options |= RES_NOAAAA; ++ ++ for (int do_stuff_txt = 0; do_stuff_txt < 2; ++do_stuff_txt) ++ { ++ queries = 0; ++ stuff_txt = do_stuff_txt; ++ ++ struct addrinfo *ai = NULL; ++ int ret; ++ ret = getaddrinfo ("example.com", "80", ++ &(struct addrinfo) ++ { ++ .ai_family = AF_UNSPEC, ++ .ai_socktype = SOCK_STREAM, ++ }, &ai); ++ ++ char *expected_result; ++ { ++ struct xmemstream mem; ++ xopen_memstream (&mem); ++ for (int i = 0; i < 200; ++i) ++ fprintf (mem.out, "address: STREAM/TCP 192.0.2.%d 80\n", i + 1); ++ xfclose_memstream (&mem); ++ expected_result = mem.buffer; ++ } ++ ++ check_addrinfo ("example.com", ai, ret, expected_result); ++ ++ free (expected_result); ++ freeaddrinfo (ai); ++ } ++ ++ resolv_test_end (obj); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-upstream-2.34-373.patch b/SOURCES/glibc-upstream-2.34-373.patch deleted file mode 100644 index 01b4007..0000000 Binary files a/SOURCES/glibc-upstream-2.34-373.patch and /dev/null differ diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index ddb8035..c792ddf 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -155,7 +155,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 60%{?dist} +Release: 60%{?dist}.7 # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -700,6 +700,26 @@ Patch468: glibc-upstream-2.34-386.patch # glibc-upstream-2.34-387.patch is a NEWS-only update. Skipped downstream. Patch469: glibc-upstream-2.34-388.patch Patch470: glibc-upstream-2.34-389.patch +# (Reverted fixes for RHEL-3385 were here.) +Patch476: glibc-rh2234715.patch +Patch477: glibc-RHEL-2437.patch +Patch478: glibc-RHEL-2425-1.patch +Patch479: glibc-RHEL-2425-2.patch +Patch480: glibc-RHEL-2425-3.patch +Patch481: glibc-RHEL-2425-4.patch +Patch482: glibc-RHEL-2425-5.patch +Patch483: glibc-RHEL-2425-6.patch +Patch484: glibc-RHEL-2425-7.patch +Patch485: glibc-RHEL-2425-8.patch +Patch486: glibc-RHEL-2425-9.patch +Patch487: glibc-RHEL-2425-10.patch +Patch488: glibc-RHEL-2425-11.patch +Patch489: glibc-RHEL-2425-12.patch +Patch490: glibc-RHEL-2425-13.patch +Patch491: glibc-RHEL-2425-14.patch +Patch492: glibc-RHEL-2999.patch +Patch493: glibc-RHEL-2425-15.patch +Patch494: glibc-RHEL-2425-16.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2859,6 +2879,27 @@ fi %endif %changelog +* Mon Sep 25 2023 Florian Weimer - 2.34-60.7 +- Fix memory leak regression in getaddrinfo (RHEL-2425) + +* Tue Sep 19 2023 Carlos O'Donell - 2.34-60.6 +- CVE-2023-4911 glibc: buffer overflow in ld.so leading to privilege escalation (RHEL-2999) + +* Tue Sep 19 2023 Carlos O'Donell - 2.34-60.5 +- Revert: Always call destructors in reverse constructor order (RHEL-3385) + +* Mon Sep 18 2023 Siddhesh Poyarekar - 2.34-60.4 +- CVE-2023-4806 glibc: potential use-after-free in getaddrinfo (RHEL-2425) + +* Fri Sep 15 2023 Siddhesh Poyarekar - 2.34-60.3 +- CVE-2023-4813: potential use-after-free in gaih_inet (RHEL-2437) + +* Fri Sep 15 2023 Carlos O'Donell - 2.34-60.2 +- CVE-2023-4527: Stack read overflow in getaddrinfo in no-aaaa mode (#2234715) + +* Wed Sep 13 2023 Florian Weimer - 2.34-60.1 +- Always call destructors in reverse constructor order (RHEL-3385) + * Wed Feb 8 2023 Florian Weimer - 2.34-60 - Upstream test for ldconfig -p (#2167811)