This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch release/2.26/master updated. glibc-2.26-61-g05155f0


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, release/2.26/master has been updated
       via  05155f0772a4befcc10da16ab64060ae7836ff7c (commit)
       via  13728f56f0cdfff536fe6673eae6881a3483a6a2 (commit)
       via  5ebb81e29243ff286bd46dea62fab46a40dfd6c3 (commit)
      from  f725563967c1f277e0f02bb1516fe9ebfa4737bf (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=05155f0772a4befcc10da16ab64060ae7836ff7c

commit 05155f0772a4befcc10da16ab64060ae7836ff7c
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Oct 19 10:44:31 2017 +0200

    nss_files: Avoid large buffers with many host addresses [BZ #22078]
    
    The previous implementation had at least a quadratic space
    requirement in the number of host addresses and aliases.
    
    (cherry picked from commit d8425e116cdd954fea0c04c0f406179b5daebbb3)

diff --git a/ChangeLog b/ChangeLog
index 40867db..ad3adc0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2017-10-11  Florian Weimer  <fweimer@redhat.com>
 
+	[BZ #22078]
+	Avoid large NSS buffers with many addresses, aliases.
+	* nss/nss_files/files-hosts.c (gethostbyname3_multi): Rewrite
+	using dynarrays and struct alloc_buffer.
+	* nss/Makefile (tests): Add tst-nss-files-hosts-multi.
+	(tst-nss-files-hosts-multi): Link with -ldl.
+	* nss/tst-nss-files-hosts-multi.c: New file.
+
+2017-10-11  Florian Weimer  <fweimer@redhat.com>
+
 	[BZ #18023]
 	* nss/nss_files/files-hosts.c (gethostbyname3_multi): Use struct
 	scratch_buffer.  Eliminate gotos.
diff --git a/NEWS b/NEWS
index d6d1f90..9cb8f00 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,7 @@ The following bugs are resolved with this release:
   [21982] string: stratcliff.c: error: assuming signed overflow does not
     occur with -O3
   [22051] libc: zero terminator in the middle of glibc's .eh_frame
+  [22078] nss_files performance issue in hosts multi mode
   [22095] resolv: Fix memory leak with OOM during resolv.conf parsing
   [22096] resolv: __resolv_conf_attach must not free passed conf object
   [22111] malloc: per thread cache is not returned when thread exits
diff --git a/nss/Makefile b/nss/Makefile
index 91b1c21..8efb2a5 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -61,6 +61,7 @@ xtests			= bug-erange
 # Tests which need libdl
 ifeq (yes,$(build-shared))
 tests += tst-nss-files-hosts-erange
+tests += tst-nss-files-hosts-multi
 endif
 
 # If we have a thread library then we can test cancellation against
@@ -161,3 +162,4 @@ $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)
 endif
 
 $(objpfx)tst-nss-files-hosts-erange: $(libdl)
+$(objpfx)tst-nss-files-hosts-multi: $(libdl)
diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c
index 763fa39..6f7cc4d 100644
--- a/nss/nss_files/files-hosts.c
+++ b/nss/nss_files/files-hosts.c
@@ -23,6 +23,7 @@
 #include <netdb.h>
 #include <resolv/resolv-internal.h>
 #include <scratch_buffer.h>
+#include <alloc_buffer.h>
 
 
 /* Get implementation for some internal functions.  */
@@ -116,24 +117,45 @@ DB_LOOKUP (hostbyaddr, ,,,
 	   }, const void *addr, socklen_t len, int af)
 #undef EXTRA_ARGS_VALUE
 
+/* Type of the address and alias arrays.  */
+#define DYNARRAY_STRUCT array
+#define DYNARRAY_ELEMENT char *
+#define DYNARRAY_PREFIX array_
+#include <malloc/dynarray-skeleton.c>
+
 static enum nss_status
 gethostbyname3_multi (FILE * stream, const char *name, int af,
 		      struct hostent *result, char *buffer, size_t buflen,
 		      int *errnop, int *herrnop, int flags)
 {
+  assert (af == AF_INET || af == AF_INET6);
+
   /* We have to get all host entries from the file.  */
   struct scratch_buffer tmp_buffer;
   scratch_buffer_init (&tmp_buffer);
   struct hostent tmp_result_buf;
-  int naddrs = 1;
-  int naliases = 0;
-  char *bufferend;
+  struct array addresses;
+  array_init (&addresses);
+  struct array aliases;
+  array_init (&aliases);
   enum nss_status status;
 
-  while (result->h_aliases[naliases] != NULL)
-    ++naliases;
-
-  bufferend = (char *) &result->h_aliases[naliases + 1];
+  /* Preserve the addresses and aliases encountered so far.  */
+  for (size_t i = 0; result->h_addr_list[i] != NULL; ++i)
+    array_add (&addresses, result->h_addr_list[i]);
+  for (size_t i = 0; result->h_aliases[i] != NULL; ++i)
+    array_add (&aliases, result->h_aliases[i]);
+
+  /* The output buffer re-uses now-unused space at the end of the
+     buffer, starting with the aliases array.  It comes last in the
+     data produced by internal_getent.  (The alias names themselves
+     are still located in the line read in internal_getent, which is
+     stored at the beginning of the buffer.)  */
+  struct alloc_buffer outbuf;
+  {
+    char *bufferend = (char *) result->h_aliases;
+    outbuf = alloc_buffer_create (bufferend, buffer + buflen - bufferend);
+  }
 
   while (true)
     {
@@ -170,110 +192,81 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,
 	    }
 	  while ((matches = 0));
 
+	  /* If the line matches, we need to copy the addresses and
+	     aliases, so that we can reuse tmp_buffer for the next
+	     line.  */
 	  if (matches)
 	    {
-	      /* We could be very clever and try to recycle a few bytes
-		 in the buffer instead of generating new arrays.  But
-		 we are not doing this here since it's more work than
-		 it's worth.  Simply let the user provide a bit bigger
-		 buffer.  */
-	      char **new_h_addr_list;
-	      char **new_h_aliases;
-	      int newaliases = 0;
-	      size_t newstrlen = 0;
-	      int cnt;
-
-	      /* Count the new aliases and the length of the strings.  */
-	      while (tmp_result_buf.h_aliases[newaliases] != NULL)
+	      /* Record the addresses.  */
+	      for (size_t i = 0; tmp_result_buf.h_addr_list[i] != NULL; ++i)
 		{
-		  char *cp = tmp_result_buf.h_aliases[newaliases];
-		  ++newaliases;
-		  newstrlen += strlen (cp) + 1;
+		  /* Allocate the target space in the output buffer,
+		     depending on the address family.  */
+		  void *target;
+		  if (af == AF_INET)
+		    {
+		      assert (tmp_result_buf.h_length == 4);
+		      target = alloc_buffer_alloc (&outbuf, struct in_addr);
+		    }
+		  else if (af == AF_INET6)
+		    {
+		      assert (tmp_result_buf.h_length == 16);
+		      target = alloc_buffer_alloc (&outbuf, struct in6_addr);
+		    }
+		  else
+		    __builtin_unreachable ();
+
+		  if (target == NULL)
+		    {
+		      /* Request a larger output buffer.  */
+		      *errnop = ERANGE;
+		      *herrnop = NETDB_INTERNAL;
+		      status = NSS_STATUS_TRYAGAIN;
+		      break;
+		    }
+		  memcpy (target, tmp_result_buf.h_addr_list[i],
+			  tmp_result_buf.h_length);
+		  array_add (&addresses, target);
 		}
-	      /* If the real name is different add it also to the
-		 aliases.  This means that there is a duplication
-		 in the alias list but this is really the user's
-		 problem.  */
-	      if (strcmp (old_result->h_name,
-			  tmp_result_buf.h_name) != 0)
+
+	      /* Record the aliases.  */
+	      for (size_t i = 0; tmp_result_buf.h_aliases[i] != NULL; ++i)
 		{
-		  ++newaliases;
-		  newstrlen += strlen (tmp_result_buf.h_name) + 1;
+		  char *alias = tmp_result_buf.h_aliases[i];
+		  array_add (&aliases,
+			     alloc_buffer_copy_string (&outbuf, alias));
 		}
 
-	      /* Make sure bufferend is aligned.  */
-	      assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
-
-	      /* Now we can check whether the buffer is large enough.
-		 16 is the maximal size of the IP address.  */
-	      if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
-		  + roundup (newstrlen, sizeof (char *))
-		  + (naliases + newaliases + 1) * sizeof (char *)
-		  >= buffer + buflen)
+	      /* If the real name is different add, it also to the
+		 aliases.  This means that there is a duplication in
+		 the alias list but this is really the user's
+		 problem.  */
+	      {
+		char *new_name = tmp_result_buf.h_name;
+		if (strcmp (old_result->h_name, new_name) != 0)
+		  array_add (&aliases,
+			     alloc_buffer_copy_string (&outbuf, new_name));
+	      }
+
+	      /* Report memory allocation failures during the
+		 expansion of the temporary arrays.  */
+	      if (array_has_failed (&addresses) || array_has_failed (&aliases))
 		{
-		  *errnop = ERANGE;
+		  *errnop = ENOMEM;
 		  *herrnop = NETDB_INTERNAL;
-		  status = NSS_STATUS_TRYAGAIN;
+		  status = NSS_STATUS_UNAVAIL;
 		  break;
 		}
 
-	      new_h_addr_list =
-		(char **) (bufferend
-			   + roundup (newstrlen, sizeof (char *))
-			   + 16);
-	      new_h_aliases =
-		(char **) ((char *) new_h_addr_list
-			   + (naddrs + 2) * sizeof (char *));
-
-	      /* Copy the old data in the new arrays.  */
-	      for (cnt = 0; cnt < naddrs; ++cnt)
-		new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
-
-	      for (cnt = 0; cnt < naliases; ++cnt)
-		new_h_aliases[cnt] = old_result->h_aliases[cnt];
-
-	      /* Store the new strings.  */
-	      cnt = 0;
-	      while (tmp_result_buf.h_aliases[cnt] != NULL)
+	      /* Request a larger output buffer if we ran out of room.  */
+	      if (alloc_buffer_has_failed (&outbuf))
 		{
-		  new_h_aliases[naliases++] = bufferend;
-		  bufferend = (__stpcpy (bufferend,
-					 tmp_result_buf.h_aliases[cnt])
-			       + 1);
-		  ++cnt;
-		}
-
-	      if (cnt < newaliases)
-		{
-		  new_h_aliases[naliases++] = bufferend;
-		  bufferend = __stpcpy (bufferend,
-					tmp_result_buf.h_name) + 1;
+		  *errnop = ERANGE;
+		  *herrnop = NETDB_INTERNAL;
+		  status = NSS_STATUS_TRYAGAIN;
+		  break;
 		}
 
-	      /* Final NULL pointer.  */
-	      new_h_aliases[naliases] = NULL;
-
-	      /* Round up the buffer end address.  */
-	      bufferend += (sizeof (char *)
-			    - ((bufferend - (char *) 0)
-			       % sizeof (char *))) % sizeof (char *);
-
-	      /* Now the new address.  */
-	      new_h_addr_list[naddrs++] =
-		memcpy (bufferend, tmp_result_buf.h_addr,
-			tmp_result_buf.h_length);
-
-	      /* Also here a final NULL pointer.  */
-	      new_h_addr_list[naddrs] = NULL;
-
-	      /* Store the new array pointers.  */
-	      old_result->h_aliases = new_h_aliases;
-	      old_result->h_addr_list = new_h_addr_list;
-
-	      /* Compute the new buffer end.  */
-	      bufferend = (char *) &new_h_aliases[naliases + 1];
-	      assert (bufferend <= buffer + buflen);
-
 	      result = old_result;
 	    } /* If match was found.  */
 
@@ -293,7 +286,47 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,
   if (status != NSS_STATUS_TRYAGAIN)
     status = NSS_STATUS_SUCCESS;
 
+  if (status == NSS_STATUS_SUCCESS)
+    {
+      /* Copy the address and alias arrays into the output buffer and
+	 add NULL terminators.  The pointed-to elements were directly
+	 written into the output buffer above and do not need to be
+	 copied again.  */
+      size_t addresses_count = array_size (&addresses);
+      size_t aliases_count = array_size (&aliases);
+      char **out_addresses = alloc_buffer_alloc_array
+	(&outbuf, char *, addresses_count + 1);
+      char **out_aliases = alloc_buffer_alloc_array
+	(&outbuf, char *, aliases_count + 1);
+      if (out_addresses == NULL || out_aliases == NULL)
+	{
+	  /* The output buffer is not large enough.  */
+	  *errnop = ERANGE;
+	  *herrnop = NETDB_INTERNAL;
+	  status = NSS_STATUS_TRYAGAIN;
+	  /* Fall through to function exit.  */
+	}
+      else
+	{
+	  /* Everything is allocated in place.  Make the copies and
+	     adjust the array pointers.  */
+	  memcpy (out_addresses, array_begin (&addresses),
+		  addresses_count * sizeof (char *));
+	  out_addresses[addresses_count] = NULL;
+	  memcpy (out_aliases, array_begin (&aliases),
+		  aliases_count * sizeof (char *));
+	  out_aliases[aliases_count] = NULL;
+
+	  result->h_addr_list = out_addresses;
+	  result->h_aliases = out_aliases;
+
+	  status = NSS_STATUS_SUCCESS;
+	}
+    }
+
   scratch_buffer_free (&tmp_buffer);
+  array_free (&addresses);
+  array_free (&aliases);
   return status;
 }
 
diff --git a/nss/tst-nss-files-hosts-multi.c b/nss/tst-nss-files-hosts-multi.c
new file mode 100644
index 0000000..195a19b
--- /dev/null
+++ b/nss/tst-nss-files-hosts-multi.c
@@ -0,0 +1,331 @@
+/* Parse /etc/hosts in multi mode with many addresses/aliases.
+   Copyright (C) 2017 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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <gnu/lib-names.h>
+#include <netdb.h>
+#include <nss.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/test-driver.h>
+#include <support/xmemstream.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+#include <sys/resource.h>
+
+struct support_chroot *chroot_env;
+
+static void
+prepare (int argc, char **argv)
+{
+  chroot_env = support_chroot_create
+    ((struct support_chroot_configuration)
+     {
+       .resolv_conf = "",
+       .hosts = "",             /* See write_hosts below.  */
+       .host_conf = "multi on\n",
+     });
+}
+
+/* Create the /etc/hosts file from outside the chroot.  */
+static void
+write_hosts (int count)
+{
+  TEST_VERIFY (count > 0 && count <= 65535);
+  FILE *fp = xfopen (chroot_env->path_hosts, "w");
+  fputs ("127.0.0.1   localhost localhost.localdomain\n"
+         "::1         localhost localhost.localdomain\n",
+         fp);
+  for (int i = 0; i < count; ++i)
+    {
+      fprintf (fp, "10.4.%d.%d www4.example.com\n",
+               (i / 256) & 0xff, i & 0xff);
+      fprintf (fp, "10.46.%d.%d www.example.com\n",
+               (i / 256) & 0xff, i & 0xff);
+      fprintf (fp, "192.0.2.1 alias.example.com v4-%d.example.com\n", i);
+      fprintf (fp, "2001:db8::6:%x www6.example.com\n", i);
+      fprintf (fp, "2001:db8::46:%x www.example.com\n", i);
+      fprintf (fp, "2001:db8::1 alias.example.com v6-%d.example.com\n", i);
+    }
+  xfclose (fp);
+}
+
+/* Parameters of a single test.  */
+struct test_params
+{
+  const char *name;             /* Name to query.  */
+  const char *marker;           /* Address marker for the name.  */
+  int count;                    /* Number of addresses/aliases.  */
+  int family;                   /* AF_INET, AF_INET_6 or AF_UNSPEC.  */
+  bool canonname;               /* True if AI_CANONNAME should be enabled.  */
+};
+
+/* Expected result of gethostbyname/gethostbyname2.  */
+static char *
+expected_ghbn (const struct test_params *params)
+{
+  TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);
+
+  struct xmemstream expected;
+  xopen_memstream (&expected);
+  if (strcmp (params->name, "alias.example.com") == 0)
+    {
+      fprintf (expected.out, "name: %s\n", params->name);
+      char af;
+      if (params->family == AF_INET)
+        af = '4';
+      else
+        af = '6';
+      for (int i = 0; i < params->count; ++i)
+        fprintf (expected.out, "alias: v%c-%d.example.com\n", af, i);
+
+      for (int i = 0; i < params->count; ++i)
+        if (params->family == AF_INET)
+          fputs ("address: 192.0.2.1\n", expected.out);
+        else
+          fputs ("address: 2001:db8::1\n", expected.out);
+    }
+  else /* www/www4/www6 name.  */
+    {
+      bool do_ipv4 = params->family == AF_INET
+        && strncmp (params->name, "www6", 4) != 0;
+      bool do_ipv6 = params->family == AF_INET6
+        && strncmp (params->name, "www4", 4) != 0;
+      if (do_ipv4 || do_ipv6)
+        {
+          fprintf (expected.out, "name: %s\n", params->name);
+          if (do_ipv4)
+            for (int i = 0; i < params->count; ++i)
+              fprintf (expected.out, "address: 10.%s.%d.%d\n",
+                       params->marker, i / 256, i % 256);
+          if (do_ipv6)
+            for (int i = 0; i < params->count; ++i)
+              fprintf (expected.out, "address: 2001:db8::%s:%x\n",
+                       params->marker, i);
+        }
+      else
+        fputs ("error: HOST_NOT_FOUND\n", expected.out);
+    }
+  xfclose_memstream (&expected);
+  return expected.buffer;
+}
+
+/* Expected result of getaddrinfo.  */
+static char *
+expected_gai (const struct test_params *params)
+{
+  bool do_ipv4 = false;
+  bool do_ipv6 = false;
+  if (params->family == AF_UNSPEC)
+    do_ipv4 = do_ipv6 = true;
+  else if (params->family == AF_INET)
+    do_ipv4 = true;
+  else if (params->family == AF_INET6)
+    do_ipv6 = true;
+
+  struct xmemstream expected;
+  xopen_memstream (&expected);
+  if (strcmp (params->name, "alias.example.com") == 0)
+    {
+      if (params->canonname)
+        fprintf (expected.out,
+                 "flags: AI_CANONNAME\n"
+                 "canonname: %s\n",
+                 params->name);
+
+      if (do_ipv4)
+        for (int i = 0; i < params->count; ++i)
+          fputs ("address: STREAM/TCP 192.0.2.1 80\n", expected.out);
+      if (do_ipv6)
+        for (int i = 0; i < params->count; ++i)
+          fputs ("address: STREAM/TCP 2001:db8::1 80\n", expected.out);
+    }
+  else /* www/www4/www6 name.  */
+    {
+      if (strncmp (params->name, "www4", 4) == 0)
+        do_ipv6 = false;
+      else if (strncmp (params->name, "www6", 4) == 0)
+        do_ipv4 = false;
+      /* Otherwise, we have www as the name, so we do both.  */
+
+      if (do_ipv4 || do_ipv6)
+        {
+          if (params->canonname)
+            fprintf (expected.out,
+                     "flags: AI_CANONNAME\n"
+                     "canonname: %s\n",
+                     params->name);
+
+          if (do_ipv4)
+            for (int i = 0; i < params->count; ++i)
+              fprintf (expected.out, "address: STREAM/TCP 10.%s.%d.%d 80\n",
+                       params->marker, i / 256, i % 256);
+          if (do_ipv6)
+            for (int i = 0; i < params->count; ++i)
+              fprintf (expected.out,
+                       "address: STREAM/TCP 2001:db8::%s:%x 80\n",
+                       params->marker, i);
+        }
+      else
+        fputs ("error: Name or service not known\n", expected.out);
+    }
+  xfclose_memstream (&expected);
+  return expected.buffer;
+}
+
+static void
+run_gbhn_gai (struct test_params *params)
+{
+  char *ctx = xasprintf ("name=%s marker=%s count=%d family=%d",
+                         params->name, params->marker, params->count,
+                         params->family);
+  if (test_verbose > 0)
+    printf ("info: %s\n", ctx);
+
+  /* Check gethostbyname, gethostbyname2.  */
+  if (params->family == AF_INET)
+    {
+      char *expected = expected_ghbn (params);
+      check_hostent (ctx, gethostbyname (params->name), expected);
+      free (expected);
+    }
+  if (params->family != AF_UNSPEC)
+    {
+      char *expected = expected_ghbn (params);
+      check_hostent (ctx, gethostbyname2 (params->name, params->family),
+                     expected);
+      free (expected);
+    }
+
+  /* Check getaddrinfo.  */
+  for (int do_canonical = 0; do_canonical < 2; ++do_canonical)
+    {
+      params->canonname = do_canonical;
+      char *expected = expected_gai (params);
+      struct addrinfo hints =
+        {
+          .ai_family = params->family,
+          .ai_socktype = SOCK_STREAM,
+          .ai_protocol = IPPROTO_TCP,
+        };
+      if (do_canonical)
+        hints.ai_flags |= AI_CANONNAME;
+      struct addrinfo *ai;
+      int ret = getaddrinfo (params->name, "80", &hints, &ai);
+      check_addrinfo (ctx, ai, ret, expected);
+      if (ret == 0)
+        freeaddrinfo (ai);
+      free (expected);
+    }
+
+  free (ctx);
+}
+
+/* Callback for the subprocess which runs the test in a chroot.  */
+static void
+subprocess (void *closure)
+{
+  struct test_params *params = closure;
+
+  xchroot (chroot_env->path_chroot);
+
+  static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };
+  static const char *const names[] =
+    {
+      "www.example.com", "www4.example.com", "www6.example.com",
+      "alias.example.com",
+      NULL
+    };
+  static const char *const names_marker[] = { "46", "4", "6", "" };
+
+  for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)
+    {
+      params->family = families[family_idx];
+      for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)
+        {
+          params->name = names[names_idx];
+          params->marker = names_marker[names_idx];
+          run_gbhn_gai (params);
+        }
+    }
+}
+
+/* Run the test for a specific number of addresses/aliases.  */
+static void
+run_test (int count)
+{
+  write_hosts (count);
+
+  struct test_params params =
+    {
+      .count = count,
+    };
+
+  support_isolate_in_subprocess (subprocess, &params);
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  if (!support_can_chroot ())
+    return EXIT_UNSUPPORTED;
+
+  /* This test should not use gigabytes of memory.   */
+  {
+    struct rlimit limit;
+    if (getrlimit (RLIMIT_AS, &limit) != 0)
+      {
+        printf ("getrlimit (RLIMIT_AS) failed: %m\n");
+        return 1;
+      }
+    long target = 200 * 1024 * 1024;
+    if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
+      {
+        limit.rlim_cur = target;
+        if (setrlimit (RLIMIT_AS, &limit) != 0)
+          {
+            printf ("setrlimit (RLIMIT_AS) failed: %m\n");
+            return 1;
+          }
+      }
+  }
+
+  __nss_configure_lookup ("hosts", "files");
+  if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
+    FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
+
+  /* Run the tests with a few different address/alias counts.  */
+  for (int count = 1; count <= 111; ++count)
+    run_test (count);
+  run_test (1111);
+  run_test (22222);
+
+  support_chroot_free (chroot_env);
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=13728f56f0cdfff536fe6673eae6881a3483a6a2

commit 13728f56f0cdfff536fe6673eae6881a3483a6a2
Author: Florian Weimer <fweimer@redhat.com>
Date:   Wed Oct 11 07:01:34 2017 +0200

    nss_files: Use struct scratch_buffer for gethostbyname [BZ #18023]
    
    (cherry picked from commit 78e806fd8cd8c918d3bbe1bcdf9091ab365e4a69)

diff --git a/ChangeLog b/ChangeLog
index 54347f5..40867db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2017-10-11  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #18023]
+	* nss/nss_files/files-hosts.c (gethostbyname3_multi): Use struct
+	scratch_buffer.  Eliminate gotos.
+
 2017-10-10  Florian Weimer  <fweimer@redhat.com>
 
 	* nss/nss_files/files-hosts.c (gethostbyname3_multi): New
diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c
index 867c10c..763fa39 100644
--- a/nss/nss_files/files-hosts.c
+++ b/nss/nss_files/files-hosts.c
@@ -22,6 +22,7 @@
 #include <arpa/nameser.h>
 #include <netdb.h>
 #include <resolv/resolv-internal.h>
+#include <scratch_buffer.h>
 
 
 /* Get implementation for some internal functions.  */
@@ -121,15 +122,12 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,
 		      int *errnop, int *herrnop, int flags)
 {
   /* We have to get all host entries from the file.  */
-  size_t tmp_buflen = MIN (buflen, 4096);
-  char tmp_buffer_stack[tmp_buflen]
-    __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
-  char *tmp_buffer = tmp_buffer_stack;
+  struct scratch_buffer tmp_buffer;
+  scratch_buffer_init (&tmp_buffer);
   struct hostent tmp_result_buf;
   int naddrs = 1;
   int naliases = 0;
   char *bufferend;
-  bool tmp_buffer_malloced = false;
   enum nss_status status;
 
   while (result->h_aliases[naliases] != NULL)
@@ -137,181 +135,165 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,
 
   bufferend = (char *) &result->h_aliases[naliases + 1];
 
- again:
-  while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
-				    tmp_buflen, errnop, herrnop, af,
-				    flags))
-	 == NSS_STATUS_SUCCESS)
+  while (true)
     {
-      int matches = 1;
-      struct hostent *old_result = result;
-      result = &tmp_result_buf;
-      /* The following piece is a bit clumsy but we want to use the
-	 `LOOKUP_NAME_CASE' value.  The optimizer should do its
-	 job.  */
-      do
+      status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,
+				tmp_buffer.length, errnop, herrnop, af,
+				flags);
+      /* Enlarge the buffer if necessary.  */
+      if (status == NSS_STATUS_TRYAGAIN && *herrnop == NETDB_INTERNAL
+	  && *errnop == ERANGE)
 	{
-	  LOOKUP_NAME_CASE (h_name, h_aliases)
-	    result = old_result;
+	  if (!scratch_buffer_grow (&tmp_buffer))
+	    {
+	      *errnop = ENOMEM;
+	      /* *herrnop and status already have the right value.  */
+	      break;
+	    }
+	  /* Loop around and retry with a larger buffer.  */
 	}
-      while ((matches = 0));
-
-      if (matches)
+      else if (status == NSS_STATUS_SUCCESS)
 	{
-	  /* We could be very clever and try to recycle a few bytes
-	     in the buffer instead of generating new arrays.  But
-	     we are not doing this here since it's more work than
-	     it's worth.  Simply let the user provide a bit bigger
-	     buffer.  */
-	  char **new_h_addr_list;
-	  char **new_h_aliases;
-	  int newaliases = 0;
-	  size_t newstrlen = 0;
-	  int cnt;
-
-	  /* Count the new aliases and the length of the strings.  */
-	  while (tmp_result_buf.h_aliases[newaliases] != NULL)
+	  /* A line was read.  Check that it matches the search
+	     criteria.  */
+
+	  int matches = 1;
+	  struct hostent *old_result = result;
+	  result = &tmp_result_buf;
+	  /* The following piece is a bit clumsy but we want to use
+	     the `LOOKUP_NAME_CASE' value.  The optimizer should do
+	     its job.  */
+	  do
 	    {
-	      char *cp = tmp_result_buf.h_aliases[newaliases];
-	      ++newaliases;
-	      newstrlen += strlen (cp) + 1;
+	      LOOKUP_NAME_CASE (h_name, h_aliases)
+		result = old_result;
 	    }
-	  /* If the real name is different add it also to the
-	     aliases.  This means that there is a duplication
-	     in the alias list but this is really the user's
-	     problem.  */
-	  if (strcmp (old_result->h_name,
-		      tmp_result_buf.h_name) != 0)
+	  while ((matches = 0));
+
+	  if (matches)
 	    {
-	      ++newaliases;
-	      newstrlen += strlen (tmp_result_buf.h_name) + 1;
-	    }
+	      /* We could be very clever and try to recycle a few bytes
+		 in the buffer instead of generating new arrays.  But
+		 we are not doing this here since it's more work than
+		 it's worth.  Simply let the user provide a bit bigger
+		 buffer.  */
+	      char **new_h_addr_list;
+	      char **new_h_aliases;
+	      int newaliases = 0;
+	      size_t newstrlen = 0;
+	      int cnt;
+
+	      /* Count the new aliases and the length of the strings.  */
+	      while (tmp_result_buf.h_aliases[newaliases] != NULL)
+		{
+		  char *cp = tmp_result_buf.h_aliases[newaliases];
+		  ++newaliases;
+		  newstrlen += strlen (cp) + 1;
+		}
+	      /* If the real name is different add it also to the
+		 aliases.  This means that there is a duplication
+		 in the alias list but this is really the user's
+		 problem.  */
+	      if (strcmp (old_result->h_name,
+			  tmp_result_buf.h_name) != 0)
+		{
+		  ++newaliases;
+		  newstrlen += strlen (tmp_result_buf.h_name) + 1;
+		}
 
-	  /* Make sure bufferend is aligned.  */
-	  assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
+	      /* Make sure bufferend is aligned.  */
+	      assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
 
-	  /* Now we can check whether the buffer is large enough.
-	     16 is the maximal size of the IP address.  */
-	  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
-	      + roundup (newstrlen, sizeof (char *))
-	      + (naliases + newaliases + 1) * sizeof (char *)
-	      >= buffer + buflen)
-	    {
-	      *errnop = ERANGE;
-	      *herrnop = NETDB_INTERNAL;
-	      status = NSS_STATUS_TRYAGAIN;
-	      goto out;
-	    }
+	      /* Now we can check whether the buffer is large enough.
+		 16 is the maximal size of the IP address.  */
+	      if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
+		  + roundup (newstrlen, sizeof (char *))
+		  + (naliases + newaliases + 1) * sizeof (char *)
+		  >= buffer + buflen)
+		{
+		  *errnop = ERANGE;
+		  *herrnop = NETDB_INTERNAL;
+		  status = NSS_STATUS_TRYAGAIN;
+		  break;
+		}
 
-	  new_h_addr_list =
-	    (char **) (bufferend
-		       + roundup (newstrlen, sizeof (char *))
-		       + 16);
-	  new_h_aliases =
-	    (char **) ((char *) new_h_addr_list
-		       + (naddrs + 2) * sizeof (char *));
+	      new_h_addr_list =
+		(char **) (bufferend
+			   + roundup (newstrlen, sizeof (char *))
+			   + 16);
+	      new_h_aliases =
+		(char **) ((char *) new_h_addr_list
+			   + (naddrs + 2) * sizeof (char *));
 
-	  /* Copy the old data in the new arrays.  */
-	  for (cnt = 0; cnt < naddrs; ++cnt)
-	    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
+	      /* Copy the old data in the new arrays.  */
+	      for (cnt = 0; cnt < naddrs; ++cnt)
+		new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
 
-	  for (cnt = 0; cnt < naliases; ++cnt)
-	    new_h_aliases[cnt] = old_result->h_aliases[cnt];
+	      for (cnt = 0; cnt < naliases; ++cnt)
+		new_h_aliases[cnt] = old_result->h_aliases[cnt];
 
-	  /* Store the new strings.  */
-	  cnt = 0;
-	  while (tmp_result_buf.h_aliases[cnt] != NULL)
-	    {
-	      new_h_aliases[naliases++] = bufferend;
-	      bufferend = (__stpcpy (bufferend,
-				     tmp_result_buf.h_aliases[cnt])
-			   + 1);
-	      ++cnt;
-	    }
+	      /* Store the new strings.  */
+	      cnt = 0;
+	      while (tmp_result_buf.h_aliases[cnt] != NULL)
+		{
+		  new_h_aliases[naliases++] = bufferend;
+		  bufferend = (__stpcpy (bufferend,
+					 tmp_result_buf.h_aliases[cnt])
+			       + 1);
+		  ++cnt;
+		}
 
-	  if (cnt < newaliases)
-	    {
-	      new_h_aliases[naliases++] = bufferend;
-	      bufferend = __stpcpy (bufferend,
-				    tmp_result_buf.h_name) + 1;
-	    }
+	      if (cnt < newaliases)
+		{
+		  new_h_aliases[naliases++] = bufferend;
+		  bufferend = __stpcpy (bufferend,
+					tmp_result_buf.h_name) + 1;
+		}
 
-	  /* Final NULL pointer.  */
-	  new_h_aliases[naliases] = NULL;
+	      /* Final NULL pointer.  */
+	      new_h_aliases[naliases] = NULL;
 
-	  /* Round up the buffer end address.  */
-	  bufferend += (sizeof (char *)
-			- ((bufferend - (char *) 0)
-			   % sizeof (char *))) % sizeof (char *);
+	      /* Round up the buffer end address.  */
+	      bufferend += (sizeof (char *)
+			    - ((bufferend - (char *) 0)
+			       % sizeof (char *))) % sizeof (char *);
 
-	  /* Now the new address.  */
-	  new_h_addr_list[naddrs++] =
-	    memcpy (bufferend, tmp_result_buf.h_addr,
-		    tmp_result_buf.h_length);
+	      /* Now the new address.  */
+	      new_h_addr_list[naddrs++] =
+		memcpy (bufferend, tmp_result_buf.h_addr,
+			tmp_result_buf.h_length);
 
-	  /* Also here a final NULL pointer.  */
-	  new_h_addr_list[naddrs] = NULL;
+	      /* Also here a final NULL pointer.  */
+	      new_h_addr_list[naddrs] = NULL;
 
-	  /* Store the new array pointers.  */
-	  old_result->h_aliases = new_h_aliases;
-	  old_result->h_addr_list = new_h_addr_list;
+	      /* Store the new array pointers.  */
+	      old_result->h_aliases = new_h_aliases;
+	      old_result->h_addr_list = new_h_addr_list;
 
-	  /* Compute the new buffer end.  */
-	  bufferend = (char *) &new_h_aliases[naliases + 1];
-	  assert (bufferend <= buffer + buflen);
+	      /* Compute the new buffer end.  */
+	      bufferend = (char *) &new_h_aliases[naliases + 1];
+	      assert (bufferend <= buffer + buflen);
 
-	  result = old_result;
-	}
-    }
+	      result = old_result;
+	    } /* If match was found.  */
 
-  if (status == NSS_STATUS_TRYAGAIN)
-    {
-      size_t newsize = 2 * tmp_buflen;
-      if (tmp_buffer_malloced)
-	{
-	  char *newp = realloc (tmp_buffer, newsize);
-	  if (newp != NULL)
-	    {
-	      assert ((((uintptr_t) newp)
-		       & (__alignof__ (struct hostent_data) - 1))
-		      == 0);
-	      tmp_buffer = newp;
-	      tmp_buflen = newsize;
-	      goto again;
-	    }
-	}
-      else if (!__libc_use_alloca (buflen + newsize))
-	{
-	  tmp_buffer = malloc (newsize);
-	  if (tmp_buffer != NULL)
-	    {
-	      assert ((((uintptr_t) tmp_buffer)
-		       & (__alignof__ (struct hostent_data) - 1))
-		      == 0);
-	      tmp_buffer_malloced = true;
-	      tmp_buflen = newsize;
-	      goto again;
-	    }
-	}
+	  /* If no match is found, loop around and fetch another
+	     line.  */
+
+	} /* status == NSS_STATUS_SUCCESS.  */
       else
-	{
-	  tmp_buffer
-	    = extend_alloca (tmp_buffer, tmp_buflen,
-			     newsize
-			     + __alignof__ (struct hostent_data));
-	  tmp_buffer = (char *) (((uintptr_t) tmp_buffer
-				  + __alignof__ (struct hostent_data)
-				  - 1)
-				 & ~(__alignof__ (struct hostent_data)
-				     - 1));
-	  goto again;
-	}
-    }
-  else
+	/* internal_getent returned an error.  */
+	break;
+    } /* while (true) */
+
+  /* Propagate the NSS_STATUS_TRYAGAIN error to the caller.  It means
+     that we may not have loaded the complete result.
+     NSS_STATUS_NOTFOUND, however, means that we reached the end of
+     the file successfully.  */
+  if (status != NSS_STATUS_TRYAGAIN)
     status = NSS_STATUS_SUCCESS;
- out:
-  if (tmp_buffer_malloced)
-    free (tmp_buffer);
+
+  scratch_buffer_free (&tmp_buffer);
   return status;
 }
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=5ebb81e29243ff286bd46dea62fab46a40dfd6c3

commit 5ebb81e29243ff286bd46dea62fab46a40dfd6c3
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue Oct 10 11:50:41 2017 +0200

    nss_files: Refactor gethostbyname3 multi case into separate function
    
    This is in preparation of further cleanup work.
    
    (cherry picked from commit 8ed70de2faceb4bd7b35bbdc2b7e8c83d9a297ba)

diff --git a/ChangeLog b/ChangeLog
index 846601b..54347f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2017-10-10  Florian Weimer  <fweimer@redhat.com>
+
+	* nss/nss_files/files-hosts.c (gethostbyname3_multi): New
+	function.
+	(_nss_files_gethostbyname3_r): Call it.
+
 2017-09-21  Gabriel F. T. Gomes  <gabriel@inconstante.eti.br>
 
 	* sysdeps/ieee754/ldbl-128/e_lgammal_r.c (__ieee754_lgammal_r):
diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c
index bccb6a5..867c10c 100644
--- a/nss/nss_files/files-hosts.c
+++ b/nss/nss_files/files-hosts.c
@@ -115,6 +115,206 @@ DB_LOOKUP (hostbyaddr, ,,,
 	   }, const void *addr, socklen_t len, int af)
 #undef EXTRA_ARGS_VALUE
 
+static enum nss_status
+gethostbyname3_multi (FILE * stream, const char *name, int af,
+		      struct hostent *result, char *buffer, size_t buflen,
+		      int *errnop, int *herrnop, int flags)
+{
+  /* We have to get all host entries from the file.  */
+  size_t tmp_buflen = MIN (buflen, 4096);
+  char tmp_buffer_stack[tmp_buflen]
+    __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
+  char *tmp_buffer = tmp_buffer_stack;
+  struct hostent tmp_result_buf;
+  int naddrs = 1;
+  int naliases = 0;
+  char *bufferend;
+  bool tmp_buffer_malloced = false;
+  enum nss_status status;
+
+  while (result->h_aliases[naliases] != NULL)
+    ++naliases;
+
+  bufferend = (char *) &result->h_aliases[naliases + 1];
+
+ again:
+  while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
+				    tmp_buflen, errnop, herrnop, af,
+				    flags))
+	 == NSS_STATUS_SUCCESS)
+    {
+      int matches = 1;
+      struct hostent *old_result = result;
+      result = &tmp_result_buf;
+      /* The following piece is a bit clumsy but we want to use the
+	 `LOOKUP_NAME_CASE' value.  The optimizer should do its
+	 job.  */
+      do
+	{
+	  LOOKUP_NAME_CASE (h_name, h_aliases)
+	    result = old_result;
+	}
+      while ((matches = 0));
+
+      if (matches)
+	{
+	  /* We could be very clever and try to recycle a few bytes
+	     in the buffer instead of generating new arrays.  But
+	     we are not doing this here since it's more work than
+	     it's worth.  Simply let the user provide a bit bigger
+	     buffer.  */
+	  char **new_h_addr_list;
+	  char **new_h_aliases;
+	  int newaliases = 0;
+	  size_t newstrlen = 0;
+	  int cnt;
+
+	  /* Count the new aliases and the length of the strings.  */
+	  while (tmp_result_buf.h_aliases[newaliases] != NULL)
+	    {
+	      char *cp = tmp_result_buf.h_aliases[newaliases];
+	      ++newaliases;
+	      newstrlen += strlen (cp) + 1;
+	    }
+	  /* If the real name is different add it also to the
+	     aliases.  This means that there is a duplication
+	     in the alias list but this is really the user's
+	     problem.  */
+	  if (strcmp (old_result->h_name,
+		      tmp_result_buf.h_name) != 0)
+	    {
+	      ++newaliases;
+	      newstrlen += strlen (tmp_result_buf.h_name) + 1;
+	    }
+
+	  /* Make sure bufferend is aligned.  */
+	  assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
+
+	  /* Now we can check whether the buffer is large enough.
+	     16 is the maximal size of the IP address.  */
+	  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
+	      + roundup (newstrlen, sizeof (char *))
+	      + (naliases + newaliases + 1) * sizeof (char *)
+	      >= buffer + buflen)
+	    {
+	      *errnop = ERANGE;
+	      *herrnop = NETDB_INTERNAL;
+	      status = NSS_STATUS_TRYAGAIN;
+	      goto out;
+	    }
+
+	  new_h_addr_list =
+	    (char **) (bufferend
+		       + roundup (newstrlen, sizeof (char *))
+		       + 16);
+	  new_h_aliases =
+	    (char **) ((char *) new_h_addr_list
+		       + (naddrs + 2) * sizeof (char *));
+
+	  /* Copy the old data in the new arrays.  */
+	  for (cnt = 0; cnt < naddrs; ++cnt)
+	    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
+
+	  for (cnt = 0; cnt < naliases; ++cnt)
+	    new_h_aliases[cnt] = old_result->h_aliases[cnt];
+
+	  /* Store the new strings.  */
+	  cnt = 0;
+	  while (tmp_result_buf.h_aliases[cnt] != NULL)
+	    {
+	      new_h_aliases[naliases++] = bufferend;
+	      bufferend = (__stpcpy (bufferend,
+				     tmp_result_buf.h_aliases[cnt])
+			   + 1);
+	      ++cnt;
+	    }
+
+	  if (cnt < newaliases)
+	    {
+	      new_h_aliases[naliases++] = bufferend;
+	      bufferend = __stpcpy (bufferend,
+				    tmp_result_buf.h_name) + 1;
+	    }
+
+	  /* Final NULL pointer.  */
+	  new_h_aliases[naliases] = NULL;
+
+	  /* Round up the buffer end address.  */
+	  bufferend += (sizeof (char *)
+			- ((bufferend - (char *) 0)
+			   % sizeof (char *))) % sizeof (char *);
+
+	  /* Now the new address.  */
+	  new_h_addr_list[naddrs++] =
+	    memcpy (bufferend, tmp_result_buf.h_addr,
+		    tmp_result_buf.h_length);
+
+	  /* Also here a final NULL pointer.  */
+	  new_h_addr_list[naddrs] = NULL;
+
+	  /* Store the new array pointers.  */
+	  old_result->h_aliases = new_h_aliases;
+	  old_result->h_addr_list = new_h_addr_list;
+
+	  /* Compute the new buffer end.  */
+	  bufferend = (char *) &new_h_aliases[naliases + 1];
+	  assert (bufferend <= buffer + buflen);
+
+	  result = old_result;
+	}
+    }
+
+  if (status == NSS_STATUS_TRYAGAIN)
+    {
+      size_t newsize = 2 * tmp_buflen;
+      if (tmp_buffer_malloced)
+	{
+	  char *newp = realloc (tmp_buffer, newsize);
+	  if (newp != NULL)
+	    {
+	      assert ((((uintptr_t) newp)
+		       & (__alignof__ (struct hostent_data) - 1))
+		      == 0);
+	      tmp_buffer = newp;
+	      tmp_buflen = newsize;
+	      goto again;
+	    }
+	}
+      else if (!__libc_use_alloca (buflen + newsize))
+	{
+	  tmp_buffer = malloc (newsize);
+	  if (tmp_buffer != NULL)
+	    {
+	      assert ((((uintptr_t) tmp_buffer)
+		       & (__alignof__ (struct hostent_data) - 1))
+		      == 0);
+	      tmp_buffer_malloced = true;
+	      tmp_buflen = newsize;
+	      goto again;
+	    }
+	}
+      else
+	{
+	  tmp_buffer
+	    = extend_alloca (tmp_buffer, tmp_buflen,
+			     newsize
+			     + __alignof__ (struct hostent_data));
+	  tmp_buffer = (char *) (((uintptr_t) tmp_buffer
+				  + __alignof__ (struct hostent_data)
+				  - 1)
+				 & ~(__alignof__ (struct hostent_data)
+				     - 1));
+	  goto again;
+	}
+    }
+  else
+    status = NSS_STATUS_SUCCESS;
+ out:
+  if (tmp_buffer_malloced)
+    free (tmp_buffer);
+  return status;
+}
+
 enum nss_status
 _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
 			     char *buffer, size_t buflen, int *errnop,
@@ -143,199 +343,8 @@ _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
 
       if (status == NSS_STATUS_SUCCESS
 	  && _res_hconf.flags & HCONF_FLAG_MULTI)
-	{
-	  /* We have to get all host entries from the file.  */
-	  size_t tmp_buflen = MIN (buflen, 4096);
-	  char tmp_buffer_stack[tmp_buflen]
-	    __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
-	  char *tmp_buffer = tmp_buffer_stack;
-	  struct hostent tmp_result_buf;
-	  int naddrs = 1;
-	  int naliases = 0;
-	  char *bufferend;
-	  bool tmp_buffer_malloced = false;
-
-	  while (result->h_aliases[naliases] != NULL)
-	    ++naliases;
-
-	  bufferend = (char *) &result->h_aliases[naliases + 1];
-
-	again:
-	  while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
-					    tmp_buflen, errnop, herrnop, af,
-					    flags))
-		 == NSS_STATUS_SUCCESS)
-	    {
-	      int matches = 1;
-	      struct hostent *old_result = result;
-	      result = &tmp_result_buf;
-	      /* The following piece is a bit clumsy but we want to use the
-		 `LOOKUP_NAME_CASE' value.  The optimizer should do its
-		 job.  */
-	      do
-		{
-		  LOOKUP_NAME_CASE (h_name, h_aliases)
-		  result = old_result;
-		}
-	      while ((matches = 0));
-
-	      if (matches)
-		{
-		  /* We could be very clever and try to recycle a few bytes
-		     in the buffer instead of generating new arrays.  But
-		     we are not doing this here since it's more work than
-		     it's worth.  Simply let the user provide a bit bigger
-		     buffer.  */
-		  char **new_h_addr_list;
-		  char **new_h_aliases;
-		  int newaliases = 0;
-		  size_t newstrlen = 0;
-		  int cnt;
-
-		  /* Count the new aliases and the length of the strings.  */
-		  while (tmp_result_buf.h_aliases[newaliases] != NULL)
-		    {
-		      char *cp = tmp_result_buf.h_aliases[newaliases];
-		      ++newaliases;
-		      newstrlen += strlen (cp) + 1;
-		    }
-		  /* If the real name is different add it also to the
-		     aliases.  This means that there is a duplication
-		     in the alias list but this is really the user's
-		     problem.  */
-		  if (strcmp (old_result->h_name,
-			      tmp_result_buf.h_name) != 0)
-		    {
-		      ++newaliases;
-		      newstrlen += strlen (tmp_result_buf.h_name) + 1;
-		    }
-
-		  /* Make sure bufferend is aligned.  */
-		  assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
-
-		  /* Now we can check whether the buffer is large enough.
-		     16 is the maximal size of the IP address.  */
-		  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
-		      + roundup (newstrlen, sizeof (char *))
-		      + (naliases + newaliases + 1) * sizeof (char *)
-		      >= buffer + buflen)
-		    {
-		      *errnop = ERANGE;
-		      *herrnop = NETDB_INTERNAL;
-		      status = NSS_STATUS_TRYAGAIN;
-		      goto out;
-		    }
-
-		  new_h_addr_list =
-		    (char **) (bufferend
-			       + roundup (newstrlen, sizeof (char *))
-			       + 16);
-		  new_h_aliases =
-		    (char **) ((char *) new_h_addr_list
-			       + (naddrs + 2) * sizeof (char *));
-
-		  /* Copy the old data in the new arrays.  */
-		  for (cnt = 0; cnt < naddrs; ++cnt)
-		    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
-
-		  for (cnt = 0; cnt < naliases; ++cnt)
-		    new_h_aliases[cnt] = old_result->h_aliases[cnt];
-
-		  /* Store the new strings.  */
-		  cnt = 0;
-		  while (tmp_result_buf.h_aliases[cnt] != NULL)
-		    {
-		      new_h_aliases[naliases++] = bufferend;
-		      bufferend = (__stpcpy (bufferend,
-					     tmp_result_buf.h_aliases[cnt])
-				   + 1);
-		      ++cnt;
-		    }
-
-		  if (cnt < newaliases)
-		    {
-		      new_h_aliases[naliases++] = bufferend;
-		      bufferend = __stpcpy (bufferend,
-					    tmp_result_buf.h_name) + 1;
-		    }
-
-		  /* Final NULL pointer.  */
-		  new_h_aliases[naliases] = NULL;
-
-		  /* Round up the buffer end address.  */
-		  bufferend += (sizeof (char *)
-				- ((bufferend - (char *) 0)
-				   % sizeof (char *))) % sizeof (char *);
-
-		  /* Now the new address.  */
-		  new_h_addr_list[naddrs++] =
-		    memcpy (bufferend, tmp_result_buf.h_addr,
-			    tmp_result_buf.h_length);
-
-		  /* Also here a final NULL pointer.  */
-		  new_h_addr_list[naddrs] = NULL;
-
-		  /* Store the new array pointers.  */
-		  old_result->h_aliases = new_h_aliases;
-		  old_result->h_addr_list = new_h_addr_list;
-
-		  /* Compute the new buffer end.  */
-		  bufferend = (char *) &new_h_aliases[naliases + 1];
-		  assert (bufferend <= buffer + buflen);
-
-		  result = old_result;
-		}
-	    }
-
-	  if (status == NSS_STATUS_TRYAGAIN)
-	    {
-	      size_t newsize = 2 * tmp_buflen;
-	      if (tmp_buffer_malloced)
-		{
-		  char *newp = realloc (tmp_buffer, newsize);
-		  if (newp != NULL)
-		    {
-		      assert ((((uintptr_t) newp)
-			       & (__alignof__ (struct hostent_data) - 1))
-			      == 0);
-		      tmp_buffer = newp;
-		      tmp_buflen = newsize;
-		      goto again;
-		    }
-		}
-	      else if (!__libc_use_alloca (buflen + newsize))
-		{
-		  tmp_buffer = malloc (newsize);
-		  if (tmp_buffer != NULL)
-		    {
-		      assert ((((uintptr_t) tmp_buffer)
-			       & (__alignof__ (struct hostent_data) - 1))
-			      == 0);
-		      tmp_buffer_malloced = true;
-		      tmp_buflen = newsize;
-		      goto again;
-		    }
-		}
-	      else
-		{
-		  tmp_buffer
-		    = extend_alloca (tmp_buffer, tmp_buflen,
-				     newsize
-				     + __alignof__ (struct hostent_data));
-		  tmp_buffer = (char *) (((uintptr_t) tmp_buffer
-					  + __alignof__ (struct hostent_data)
-					  - 1)
-					 & ~(__alignof__ (struct hostent_data)
-					     - 1));
-		  goto again;
-		}
-	    }
-	  else
-	    status = NSS_STATUS_SUCCESS;
-	out:
-	  if (tmp_buffer_malloced)
-	    free (tmp_buffer);
-	}
+	status = gethostbyname3_multi
+	  (stream, name, af, result, buffer, buflen, errnop, herrnop, flags);
 
       internal_endent (&stream);
     }

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                       |   22 +++
 NEWS                            |    1 +
 nss/Makefile                    |    2 +
 nss/nss_files/files-hosts.c     |  400 +++++++++++++++++++++------------------
 nss/tst-nss-files-hosts-multi.c |  331 ++++++++++++++++++++++++++++++++
 5 files changed, 568 insertions(+), 188 deletions(-)
 create mode 100644 nss/tst-nss-files-hosts-multi.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]