diff --git a/.glibc.checksum b/.glibc.checksum
new file mode 100644
index 0000000000000000000000000000000000000000..8b55b82800933f3467c660ce815deb9a062994d6
--- /dev/null
+++ b/.glibc.checksum
@@ -0,0 +1 @@
+38fef98d8f9c28b50f5a40a8ac46d85bacfaf1ee12446c81521c471d083d45ef
diff --git a/.glibc.metadata b/.glibc.metadata
index 857097928b774766f3e8a3a1f5f43b6d20d9fe93..f1959eb7f7e7041d43eb8a3088f7bbdcf59fac35 100644
--- a/.glibc.metadata
+++ b/.glibc.metadata
@@ -1 +1,3 @@
-7c3b8890a6346793b6334cc5f2fea5d437d307b8 SOURCES/glibc-2.34.tar.xz
+44d26a1fe20b8853a48f470ead01e4279e869ac149b195dda4e44a195d981ab2 SOURCES/glibc-2.34.tar.xz
+a9d3c0660e6a48bb22dc77afbc7bb47ce79ca0d8c2ee8ebee4228d8d4952214e SOURCES/glibc-c-utf8-locale-2.patch
+db7430035b16972d0c556d4cf141030da5ce7f962b369d1a0e45b77a057d481d 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 0000000000000000000000000000000000000000..6e8581c9bea59d64976798e6687aa2ac0763f461
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-1.patch
@@ -0,0 +1,65 @@
+commit 51948fdf0fc0258feca719f6a88cbdcf82f2eafc
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    (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 0000000000000000000000000000000000000000..f66930728b22d42b9590841df61b34fb0f969c54
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-10.patch
@@ -0,0 +1,156 @@
+commit 4d59769087f2143f619b4b38bf93590a86f5c806
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..18763f1c5031f536faa4da67d1eb041e7f0bcaba
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-11.patch
@@ -0,0 +1,178 @@
+commit 6e3fed9d20d6b7ef4b69dd7cfcdd7bbaf1c9a9cb
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..4b7d0194946a9618c80ba2c0210462c6e6bc1fe9
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-12.patch
@@ -0,0 +1,208 @@
+commit 92478a808f477480adbc5ca3d9a4a1bc27fc13ae
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..7e31ade061367f621dd3f5bed73fd7eb34c49987
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-13.patch
@@ -0,0 +1,34 @@
+commit cc4544ef8069a14c67a46b7e8e28eff1dc102050
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..8ef60c6169057290fba8fc6d909bcdb86a92732b
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-14.patch
@@ -0,0 +1,316 @@
+commit e09ee267c03e3150c2c9ba28625ab130705a485e
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    (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
++   <https://www.gnu.org/licenses/>.  */
++
++#include <nss.h>
++#include <stdlib.h>
++#include <string.h>
++#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
++   <https://www.gnu.org/licenses/>.  */
++
++#include <nss.h>
++#include <netdb.h>
++#include <stdlib.h>
++#include <string.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#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 <support/test-driver.c>
+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 0000000000000000000000000000000000000000..03db624905762e87da4611c10c2598a204125be6
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-15.patch
@@ -0,0 +1,37 @@
+commit 57e349b1b0df1aee2dcd19dae1f324bde25ff8f0
+Author: H.J. Lu <hjl.tools@gmail.com>
+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 <carlos@redhat.com>
+
+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 0000000000000000000000000000000000000000..6aa6f79fcca4a6c5e0a129d732d9ba3ca66e9638
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-16.patch
@@ -0,0 +1,84 @@
+commit ec6b95c3303c700eb89eebeda2d7264cc184a796
+Author: Romain Geissler <romain.geissler@amadeus.com>
+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 <siddhesh@sourceware.org>
+
+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 <netdb.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <mcheck.h>
+ #include <support/check.h>
+ #include <support/xstdio.h>
+ #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 0000000000000000000000000000000000000000..5f5b0c4ae4c6ffb7d6a2b44cba250f78629f4ad7
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-2.patch
@@ -0,0 +1,41 @@
+commit 01671608a3bddde369cdd42aed12e1c019b87158
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 0000000000000000000000000000000000000000..c431d613dcac9f2d3f5f5caf6e9cea55c39e5d92
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-3.patch
@@ -0,0 +1,248 @@
+commit b195fd86c616b147dad3a63498b79e0dedb4662b
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..27582f21394ba1debcda6cedbb3e57bad18acd68
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-4.patch
@@ -0,0 +1,86 @@
+commit f7efb43738f255db32cfa4e84a491c09f6da66e2
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..427630ded86010663ed42fcadd02b338118a4cbf
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-5.patch
@@ -0,0 +1,285 @@
+commit e05e5889b8a307fe4be55b03bcbd7a1c62fc2f2d
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..f6425af69ae86d7cf26e64efbf0dac35e9833166
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-6.patch
@@ -0,0 +1,1123 @@
+commit 922f2614d69dc47922c1a8e8a08f2bd74874587e
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..c9e05816140dd964a2adfae8bfd06d9a5ef72cce
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-7.patch
@@ -0,0 +1,179 @@
+commit 3b5a3e5009088a029525277f36228eeb95032358
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..253805a9bcec530aa534a6d4845ca55521b40b73
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-8.patch
@@ -0,0 +1,328 @@
+commit 5914a1d55b468ccf0fb6d997a7a4e378339df735
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..bc3766bd339815597999469babb45ab92572b0c5
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2425-9.patch
@@ -0,0 +1,673 @@
+commit ec71cb961121760f81e55af5489e658dc89e96e6
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Mon Mar 7 15:56:22 2022 +0530
+
+    gaih_inet: separate nss lookup loop into its own function
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+    (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 0000000000000000000000000000000000000000..a818861d9ddb81aaf01d88ab31e80a632355841a
--- /dev/null
+++ b/SOURCES/glibc-RHEL-2437.patch
@@ -0,0 +1,973 @@
+commit 1c37b8022e8763fedbb3f79c02e05c6acfe5a215
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+
+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
++   <https://www.gnu.org/licenses/>.  */
++
++#include <dlfcn.h>
++#include <gnu/lib-names.h>
++#include <nss.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <support/check.h>
++#include <support/format_nss.h>
++#include <support/support.h>
++#include <support/xstdio.h>
++#include <support/xunistd.h>
++
++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 <support/test-driver.c>
+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 0000000000000000000000000000000000000000..e072c7124d4bef8389a99c7cb2dd00eedc23fc98
--- /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 <siddhesh@redhat.com>
+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 <siddhesh@sourceware.org>
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+---
+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 7064b8e810e41f055a3a4a90eac8e8fc4a8342cb..0000000000000000000000000000000000000000
--- a/SOURCES/glibc-c-utf8-locale-2.patch
+++ /dev/null
@@ -1,1437 +0,0 @@
-commit 466f2be6c08070e9113ae2fdc7acd5d8828cba50
-Author: Carlos O'Donell <carlos@redhat.com>
-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 <fweimer@redhat.com>
-
-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
-+   <https://www.gnu.org/licenses/>.  */
-+
-+#include <iconv.h>
-+#include <stddef.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <support/support.h>
-+#include <support/check.h>
-+
-+/* 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 <support/test-driver.c>
-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 @@
-+ ; <U1>
-+ ; <U2>
-+ ; <U3>
-+ ; <U4>
-+ ; <U5>
-+ ; <U6>
-+ ; <U7>
-+ ; <U8>
-+ ; <UE>
-+ ; <UF>
-+ ; <U10>
-+ ; <U11>
-+ ; <U12>
-+ ; <U13>
-+ ; <U14>
-+ ; <U15>
-+ ; <U16>
-+ ; <U17>
-+ ; <U18>
-+ ; <U19>
-+ ; <U1A>
-+ ; <U1B>
-+ ; <U1C>
-+ ; <U1D>
-+ ; <U1E>
-+ ; <U1F>
-+! ; <U21>
-+" ; <U22>
-+# ; <U23>
-+$ ; <U24>
-+% ; <U25>
-+& ; <U26>
-+' ; <U27>
-+) ; <U29>
-+* ; <U2A>
-++ ; <U2B>
-+, ; <U2C>
-+- ; <U2D>
-+. ; <U2E>
-+/ ; <U2F>
-+0 ; <U30>
-+1 ; <U31>
-+2 ; <U32>
-+3 ; <U33>
-+4 ; <U34>
-+5 ; <U35>
-+6 ; <U36>
-+7 ; <U37>
-+8 ; <U38>
-+9 ; <U39>
-+< ; <U3C>
-+= ; <U3D>
-+> ; <U3E>
-+? ; <U3F>
-+@ ; <U40>
-+A ; <U41>
-+B ; <U42>
-+C ; <U43>
-+D ; <U44>
-+E ; <U45>
-+F ; <U46>
-+G ; <U47>
-+H ; <U48>
-+I ; <U49>
-+J ; <U4A>
-+K ; <U4B>
-+L ; <U4C>
-+M ; <U4D>
-+N ; <U4E>
-+O ; <U4F>
-+P ; <U50>
-+Q ; <U51>
-+R ; <U52>
-+S ; <U53>
-+T ; <U54>
-+U ; <U55>
-+V ; <U56>
-+W ; <U57>
-+X ; <U58>
-+Y ; <U59>
-+Z ; <U5A>
-+[ ; <U5B>
-+\ ; <U5C>
-+] ; <U5D>
-+^ ; <U5E>
-+_ ; <U5F>
-+` ; <U60>
-+a ; <U61>
-+b ; <U62>
-+c ; <U63>
-+d ; <U64>
-+e ; <U65>
-+f ; <U66>
-+g ; <U67>
-+h ; <U68>
-+i ; <U69>
-+j ; <U6A>
-+k ; <U6B>
-+l ; <U6C>
-+m ; <U6D>
-+n ; <U6E>
-+o ; <U6F>
-+p ; <U70>
-+q ; <U71>
-+r ; <U72>
-+s ; <U73>
-+t ; <U74>
-+u ; <U75>
-+v ; <U76>
-+w ; <U77>
-+x ; <U78>
-+y ; <U79>
-+z ; <U7A>
-+{ ; <U7B>
-+| ; <U7C>
-+} ; <U7D>
-+~ ; <U7E>
-+ ; <U7F>
-+€ ; <U80>
-+ÿ ; <UFF>
-+Ä€ ; <U100>
-+à¿¿ ; <UFFF>
-+က ; <U1000>
-+� ; <UFFFD>
-+ï¿¿ ; <UFFFF>
-+𐀀 ; <U10000>
-+🿿 ; <U1FFFF>
-+ð €€ ; <U20000>
-+𯿿 ; <U2FFFF>
-+ð°€€ ; <U30000>
-+ð¿¿¾ ; <U3FFFE>
-+񀀀 ; <U40000>
-+񏿿 ; <U4FFFF>
-+񐀀 ; <U50000>
-+񟿿 ; <U5FFFF>
-+ñ €€ ; <U60000>
-+񯿿 ; <U6FFFF>
-+ñ°€€ ; <U70000>
-+ñ¿¿¿ ; <U7FFFF>
-+ò€€€ ; <U80000>
-+򏿿 ; <U8FFFF>
-+򐀀 ; <U90000>
-+òŸ¿¿ ; <U9FFFF>
-+ò €€ ; <UA0000>
-+򯿿 ; <UAFFFF>
-+ò°€€ ; <UB0000>
-+ò¿¿¿ ; <UBFFFF>
-+󀀁 ; <UC0001>
-+󏿌 ; <UCFFCC>
-+󐀎 ; <UD000E>
-+óŸ¿¿ ; <UDFFFF>
-+󠀁 ; <UE0001>
-+󯿿 ; <UEFFFF>
-+󰀁 ; <UF0001>
-+ó¿¿¿ ; <UFFFFF>
-+􀀁 ; <U100001>
-+􏿿 ; <U10FFFF>
-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 <U003F>
-+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, &regex);
-+  if (s != NULL)
-+    {
-+      puts ("re_compile_pattern return non-NULL value");
-+      result = 1;
-+    }
-+  else
-+    {
-+      match = re_match (&regex, "an", 2, 0, &regs);
-+      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, &regex);
-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 <string.h>
- #include <locale.h>
- #include <libc-diag.h>
-+#include <support/support.h>
- 
- #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 <support/test-driver.c>
-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 (&regex, '\0', sizeof (regex));
- 
-+  printf ("INFO: Checking C.\n");
-   setlocale (LC_ALL, "C");
- 
-+  s = re_compile_pattern ("ab[cde]", 7, &regex);
-+  if (s != NULL)
-+    {
-+      puts ("re_compile_pattern returned non-NULL value");
-+      result = 1;
-+    }
-+  else
-+    {
-+      match[0] = re_search_2 (&regex, "xyabez", 6, "", 0, 1, 5, NULL, 6);
-+      match[1] = re_search_2 (&regex, NULL, 0, "abc", 3, 0, 3, NULL, 3);
-+      match[2] = re_search_2 (&regex, "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, &regex);
-   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, &regs[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, &regs[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 <sys/stat.h>
- #include <sys/types.h>
- #include <regex.h>
-+#include <support/support.h>
- 
- 
- #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 0000000000000000000000000000000000000000..cdf298e93887ba108ff061772742879acb99c902
--- /dev/null
+++ b/SOURCES/glibc-rh2234715.patch
@@ -0,0 +1,187 @@
+commit bd77dd7e73e3530203be1c52c8a29d08270cb25d
+Author: Florian Weimer <fweimer@redhat.com>
+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
++   <https://www.gnu.org/licenses/>.  */
++
++#include <errno.h>
++#include <netdb.h>
++#include <resolv.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <support/check.h>
++#include <support/check_nss.h>
++#include <support/resolv_test.h>
++#include <support/support.h>
++#include <support/xmemstream.h>
++
++/* 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 <support/test-driver.c>
diff --git a/SOURCES/glibc-upstream-2.34-373.patch b/SOURCES/glibc-upstream-2.34-373.patch
deleted file mode 100644
index 01b400783c45c68c1e4b6a1482da7fb1354433f3..0000000000000000000000000000000000000000
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 ddb80355947c6e362cadad7008c439ca2094fd73..c792ddf023ed7522a8a38f6160231718dd338eda 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 <fweimer@redhat.com> - 2.34-60.7
+- Fix memory leak regression in getaddrinfo (RHEL-2425)
+
+* Tue Sep 19 2023 Carlos O'Donell <carlos@redhat.com> - 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 <carlos@redhat.com> - 2.34-60.5
+- Revert: Always call destructors in reverse constructor order (RHEL-3385)
+
+* Mon Sep 18 2023 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.34-60.4
+- CVE-2023-4806 glibc: potential use-after-free in getaddrinfo (RHEL-2425)
+
+* Fri Sep 15 2023 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.34-60.3
+- CVE-2023-4813: potential use-after-free in gaih_inet (RHEL-2437)
+
+* Fri Sep 15 2023 Carlos O'Donell <carlos@redhat.com> - 2.34-60.2
+- CVE-2023-4527: Stack read overflow in getaddrinfo in no-aaaa mode (#2234715)
+
+* Wed Sep 13 2023 Florian Weimer <fweimer@redhat.com> - 2.34-60.1
+- Always call destructors in reverse constructor order (RHEL-3385)
+
 * Wed Feb  8 2023 Florian Weimer <fweimer@redhat.com> - 2.34-60
 - Upstream test for ldconfig -p (#2167811)