Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/809775/?format=api
{ "id": 809775, "url": "http://patchwork.ozlabs.org/api/patches/809775/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20170904173210.3CE87439942E3@oldenburg.str.redhat.com/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20170904173210.3CE87439942E3@oldenburg.str.redhat.com>", "list_archive_url": null, "date": "2017-09-04T17:32:10", "name": "nss_files: Avoid large buffers with many host addresses [BZ #22078]", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "41e8b2c0e4e4d657b1218ccb913835c1c07ae640", "submitter": { "id": 14312, "url": "http://patchwork.ozlabs.org/api/people/14312/?format=api", "name": "Florian Weimer", "email": "fweimer@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20170904173210.3CE87439942E3@oldenburg.str.redhat.com/mbox/", "series": [ { "id": 1430, "url": "http://patchwork.ozlabs.org/api/series/1430/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=1430", "date": "2017-09-04T17:32:10", "name": "nss_files: Avoid large buffers with many host addresses [BZ #22078]", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/1430/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/809775/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/809775/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<libc-alpha-return-84175-incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": [ "patchwork-incoming@bilbo.ozlabs.org", "mailing list libc-alpha@sourceware.org" ], "Authentication-Results": [ "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=sourceware.org\n\t(client-ip=209.132.180.131; helo=sourceware.org;\n\tenvelope-from=libc-alpha-return-84175-incoming=patchwork.ozlabs.org@sourceware.org;\n\treceiver=<UNKNOWN>)", "ozlabs.org; dkim=pass (1024-bit key;\n\tsecure) header.d=sourceware.org header.i=@sourceware.org\n\theader.b=\"efKp57g2\"; dkim-atps=neutral", "sourceware.org; auth=none", "ext-mx02.extmail.prod.ext.phx2.redhat.com;\n\tdmarc=none (p=none dis=none) header.from=redhat.com", "ext-mx02.extmail.prod.ext.phx2.redhat.com;\n\tspf=fail smtp.mailfrom=fweimer@redhat.com" ], "Received": [ "from sourceware.org (server1.sourceware.org [209.132.180.131])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xmH3f6FQZz9s7m\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 5 Sep 2017 03:32:30 +1000 (AEST)", "(qmail 33529 invoked by alias); 4 Sep 2017 17:32:20 -0000", "(qmail 33370 invoked by uid 89); 4 Sep 2017 17:32:20 -0000" ], "DomainKey-Signature": "a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id\n\t:list-unsubscribe:list-subscribe:list-archive:list-post\n\t:list-help:sender:date:to:subject:mime-version:content-type\n\t:content-transfer-encoding:message-id:from; q=dns; s=default; b=\n\tJKLK4CP8AAXB0cLyyecQPyw6O8YdxTfoWnDgl+nnGrEP9tcFsKsO1L3otMuD0AJx\n\tFTW2D6UUaatAYJmUh5RCCGJcqS9oU4e3ESyCGC4FUtQTkKmMMd5S1ydaUH/+x8q8\n\t4MlmYnnF+nVEaNd+zUnxfDx63DgAyDdOmjESCgwRQMY=", "DKIM-Signature": "v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id\n\t:list-unsubscribe:list-subscribe:list-archive:list-post\n\t:list-help:sender:date:to:subject:mime-version:content-type\n\t:content-transfer-encoding:message-id:from; s=default; bh=KDljqy\n\tWZxUGIMAxX4WuisZ7WLjw=; b=efKp57g25kU9dh67KU5DXEw/zRGdSRagR4VzxH\n\tcAIEbvoYc1kghjG+1Bt4RQ5dI+umgzja0T0iRcAC1ysaoTTXj0PkBsH2WRmo9xBd\n\t/2AizFSLMWc7AC5AxzjPxK7RKMhh02YCC1u5TmFpjNkmc1b661y+/MOsInln5zMb\n\tvPHGU=", "Mailing-List": "contact libc-alpha-help@sourceware.org; run by ezmlm", "Precedence": "bulk", "List-Id": "<libc-alpha.sourceware.org>", "List-Unsubscribe": "<mailto:libc-alpha-unsubscribe-incoming=patchwork.ozlabs.org@sourceware.org>", "List-Subscribe": "<mailto:libc-alpha-subscribe@sourceware.org>", "List-Archive": "<http://sourceware.org/ml/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-help@sourceware.org>,\n\t<http://sourceware.org/ml/#faqs>", "Sender": "libc-alpha-owner@sourceware.org", "X-Virus-Found": "No", "X-Spam-SWARE-Status": "No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0,\n\tGIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RP_MATCHES_RCVD,\n\tSPF_HELO_PASS autolearn=ham version=3.3.2 spammy=", "X-HELO": "mx1.redhat.com", "DMARC-Filter": "OpenDMARC Filter v1.3.2 mx1.redhat.com D71B8BB0C", "Date": "Mon, 04 Sep 2017 19:32:10 +0200", "To": "libc-alpha@sourceware.org", "Subject": "[PATCH] nss_files: Avoid large buffers with many host addresses\n\t[BZ #22078]", "User-Agent": "Heirloom mailx 12.5 7/5/10", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=us-ascii", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20170904173210.3CE87439942E3@oldenburg.str.redhat.com>", "From": "fweimer@redhat.com (Florian Weimer)" }, "content": "The previous implementation had at least a quadratic space\nrequirement in the number of host addresses and aliases.\n\n2017-09-04 Florian Weimer <fweimer@redhat.com>\n\n\t[BZ #22078]\n\tAvoid large NSS buffers with many addresses, aliases.\n\t* nss/nss_files/files-hosts.c (gethostbyname3_multi): Rewrite\n\tusing dynarrays and struct alloc_buffer.\n\t* nss/Makefile (tests): Add tst-nss-files-hosts-multi.\n\t(tst-nss-files-hosts-multi): Link with -ldl.\n\t* nss/tst-nss-files-hosts-multi.c: New file.", "diff": "diff --git a/nss/Makefile b/nss/Makefile\nindex c9a5200f96..ecf3530188 100644\n--- a/nss/Makefile\n+++ b/nss/Makefile\n@@ -63,6 +63,7 @@ xtests\t\t\t= bug-erange\n # Tests which need libdl\n ifeq (yes,$(build-shared))\n tests += tst-nss-files-hosts-erange\n+tests += tst-nss-files-hosts-multi\n endif\n \n # If we have a thread library then we can test cancellation against\n@@ -163,3 +164,4 @@ $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)\n endif\n \n $(objpfx)tst-nss-files-hosts-erange: $(libdl)\n+$(objpfx)tst-nss-files-hosts-multi: $(libdl)\ndiff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c\nindex c2cd07584c..f40673056b 100644\n--- a/nss/nss_files/files-hosts.c\n+++ b/nss/nss_files/files-hosts.c\n@@ -23,6 +23,7 @@\n #include <netdb.h>\n #include <resolv/resolv-internal.h>\n #include <scratch_buffer.h>\n+#include <alloc_buffer.h>\n \n \n /* Get implementation for some internal functions. */\n@@ -116,24 +117,46 @@ DB_LOOKUP (hostbyaddr, ,,,\n \t }, const void *addr, socklen_t len, int af)\n #undef EXTRA_ARGS_VALUE\n \n+/* Type of the address and alias arrays. */\n+#define DYNARRAY_STRUCT array\n+#define DYNARRAY_ELEMENT char *\n+#define DYNARRAY_PREFIX array_\n+#include <malloc/dynarray-skeleton.c>\n+\n static enum nss_status\n gethostbyname3_multi (FILE * stream, const char *name, int af,\n \t\t struct hostent *result, char *buffer, size_t buflen,\n \t\t int *errnop, int *herrnop, int flags)\n {\n+ assert (af == AF_INET || af == AF_INET6);\n+\n /* We have to get all host entries from the file. */\n struct scratch_buffer tmp_buffer;\n scratch_buffer_init (&tmp_buffer);\n struct hostent tmp_result_buf;\n- int naddrs = 1;\n- int naliases = 0;\n- char *bufferend;\n+ struct array addresses;\n+ array_init (&addresses);\n+ struct array aliases;\n+ array_init (&aliases);\n enum nss_status status;\n+ bool new_data = false;\n \n- while (result->h_aliases[naliases] != NULL)\n- ++naliases;\n+ /* The output buffer uses the unused space after the first\n+ result. */\n+ struct alloc_buffer outbuf;\n+ {\n+ /* Preserve the aliases encountered so far. */\n+ size_t i;\n+ for (i = 0; result->h_aliases[i] != NULL; ++i)\n+ array_add (&aliases, result->h_aliases[i]);\n \n- bufferend = (char *) &result->h_aliases[naliases + 1];\n+ char *bufferend = (char *) &result->h_aliases[i + 1];\n+ outbuf = alloc_buffer_create (bufferend, buffer + buflen - bufferend);\n+ }\n+\n+ /* Preserve the addresses. */\n+ for (size_t i = 0; result->h_addr_list[i] != NULL; ++i)\n+ array_add (&addresses, result->h_addr_list[i]);\n \n again:\n while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,\n@@ -154,110 +177,72 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,\n \t}\n while ((matches = 0));\n \n+ /* If the line matches, we need to copy the addresses and\n+\t aliases, so that we can reuse tmp_buffer for the next\n+\t line. */\n if (matches)\n \t{\n-\t /* We could be very clever and try to recycle a few bytes\n-\t in the buffer instead of generating new arrays. But\n-\t we are not doing this here since it's more work than\n-\t it's worth. Simply let the user provide a bit bigger\n-\t buffer. */\n-\t char **new_h_addr_list;\n-\t char **new_h_aliases;\n-\t int newaliases = 0;\n-\t size_t newstrlen = 0;\n-\t int cnt;\n+\t new_data = true;\n \n-\t /* Count the new aliases and the length of the strings. */\n-\t while (tmp_result_buf.h_aliases[newaliases] != NULL)\n+\t /* Record the addresses. */\n+\t for (int i = 0; tmp_result_buf.h_addr_list[i] != NULL; ++i)\n \t {\n-\t char *cp = tmp_result_buf.h_aliases[newaliases];\n-\t ++newaliases;\n-\t newstrlen += strlen (cp) + 1;\n+\t /* Allocate the target space in the output buffer,\n+\t\t depending on the address family. */\n+\t void *target;\n+\t if (af == AF_INET)\n+\t\t{\n+\t\t assert (tmp_result_buf.h_length == 4);\n+\t\t target = alloc_buffer_alloc (&outbuf, struct in_addr);\n+\t\t}\n+\t else if (af == AF_INET6)\n+\t\t{\n+\t\t assert (tmp_result_buf.h_length == 16);\n+\t\t target = alloc_buffer_alloc (&outbuf, struct in6_addr);\n+\t\t}\n+\t else\n+\t\t__builtin_unreachable ();\n+\n+\t if (target == NULL)\n+\t\t{\n+\t\t /* Request a larger output buffer. */\n+\t\t *errnop = ERANGE;\n+\t\t *herrnop = NETDB_INTERNAL;\n+\t\t status = NSS_STATUS_TRYAGAIN;\n+\t\t goto out;\n+\t\t}\n+\t memcpy (target, tmp_result_buf.h_addr_list[i],\n+\t\t tmp_result_buf.h_length);\n+\t array_add (&addresses, target);\n \t }\n-\t /* If the real name is different add it also to the\n-\t aliases. This means that there is a duplication\n-\t in the alias list but this is really the user's\n-\t problem. */\n-\t if (strcmp (old_result->h_name,\n-\t\t tmp_result_buf.h_name) != 0)\n+\n+\t /* Record the aliases. */\n+\t for (int i = 0; tmp_result_buf.h_aliases[i] != NULL; ++i)\n \t {\n-\t ++newaliases;\n-\t newstrlen += strlen (tmp_result_buf.h_name) + 1;\n+\t char *alias = tmp_result_buf.h_aliases[i];\n+\t array_add (&aliases, alloc_buffer_copy_string (&outbuf, alias));\n \t }\n \n-\t /* Make sure bufferend is aligned. */\n-\t assert ((bufferend - (char *) 0) % sizeof (char *) == 0);\n+\t /* If the real name is different add it also to the aliases.\n+\t This means that there is a duplication in the alias list\n+\t but this is really the user's problem. */\n+\t {\n+\t char *new_name = tmp_result_buf.h_name;\n+\t if (strcmp (old_result->h_name, new_name) != 0)\n+\t array_add (&aliases,\n+\t\t\t alloc_buffer_copy_string (&outbuf, new_name));\n+\t }\n \n-\t /* Now we can check whether the buffer is large enough.\n-\t 16 is the maximal size of the IP address. */\n-\t if (bufferend + 16 + (naddrs + 2) * sizeof (char *)\n-\t + roundup (newstrlen, sizeof (char *))\n-\t + (naliases + newaliases + 1) * sizeof (char *)\n-\t >= buffer + buflen)\n+\t /* Memory allocation failures during the expansion of the\n+\t temporary arrays. */\n+\t if (array_has_failed (&addresses) || array_has_failed (&aliases))\n \t {\n-\t *errnop = ERANGE;\n+\t *errnop = errno;\n \t *herrnop = NETDB_INTERNAL;\n-\t status = NSS_STATUS_TRYAGAIN;\n+\t status = NSS_STATUS_UNAVAIL;\n \t goto out;\n \t }\n \n-\t new_h_addr_list =\n-\t (char **) (bufferend\n-\t\t + roundup (newstrlen, sizeof (char *))\n-\t\t + 16);\n-\t new_h_aliases =\n-\t (char **) ((char *) new_h_addr_list\n-\t\t + (naddrs + 2) * sizeof (char *));\n-\n-\t /* Copy the old data in the new arrays. */\n-\t for (cnt = 0; cnt < naddrs; ++cnt)\n-\t new_h_addr_list[cnt] = old_result->h_addr_list[cnt];\n-\n-\t for (cnt = 0; cnt < naliases; ++cnt)\n-\t new_h_aliases[cnt] = old_result->h_aliases[cnt];\n-\n-\t /* Store the new strings. */\n-\t cnt = 0;\n-\t while (tmp_result_buf.h_aliases[cnt] != NULL)\n-\t {\n-\t new_h_aliases[naliases++] = bufferend;\n-\t bufferend = (__stpcpy (bufferend,\n-\t\t\t\t tmp_result_buf.h_aliases[cnt])\n-\t\t\t + 1);\n-\t ++cnt;\n-\t }\n-\n-\t if (cnt < newaliases)\n-\t {\n-\t new_h_aliases[naliases++] = bufferend;\n-\t bufferend = __stpcpy (bufferend,\n-\t\t\t\t tmp_result_buf.h_name) + 1;\n-\t }\n-\n-\t /* Final NULL pointer. */\n-\t new_h_aliases[naliases] = NULL;\n-\n-\t /* Round up the buffer end address. */\n-\t bufferend += (sizeof (char *)\n-\t\t\t- ((bufferend - (char *) 0)\n-\t\t\t % sizeof (char *))) % sizeof (char *);\n-\n-\t /* Now the new address. */\n-\t new_h_addr_list[naddrs++] =\n-\t memcpy (bufferend, tmp_result_buf.h_addr,\n-\t\t tmp_result_buf.h_length);\n-\n-\t /* Also here a final NULL pointer. */\n-\t new_h_addr_list[naddrs] = NULL;\n-\n-\t /* Store the new array pointers. */\n-\t old_result->h_aliases = new_h_aliases;\n-\t old_result->h_addr_list = new_h_addr_list;\n-\n-\t /* Compute the new buffer end. */\n-\t bufferend = (char *) &new_h_aliases[naliases + 1];\n-\t assert (bufferend <= buffer + buflen);\n-\n \t result = old_result;\n \t}\n }\n@@ -266,16 +251,52 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,\n {\n if (!scratch_buffer_grow (&tmp_buffer))\n \t{\n+\t *errnop = errno;\n \t *herrnop = NETDB_INTERNAL;\n-\t status = NSS_STATUS_TRYAGAIN;\n+\t status = NSS_STATUS_UNAVAIL;\n \t}\n else\n \tgoto again;\n }\n else\n- status = NSS_STATUS_SUCCESS;\n+ {\n+ /* Copy the address and alias arrays into the output buffer and\n+\t add NULL terminators. The pointed-to elements were directly\n+\t written into the output buffer above and do not need to be\n+\t copied again. */\n+ if (new_data)\n+\t{\n+\t size_t addresses_count = array_size (&addresses);\n+\t size_t aliases_count = array_size (&aliases);\n+\t char **out_addresses = alloc_buffer_alloc_array\n+\t (&outbuf, char *, addresses_count + 1);\n+\t char **out_aliases = alloc_buffer_alloc_array\n+\t (&outbuf, char *, aliases_count + 1);\n+\t if (out_addresses == NULL || out_aliases == NULL)\n+\t {\n+\t /* The output buffer is not large enough. */\n+\t *errnop = ERANGE;\n+\t *herrnop = NETDB_INTERNAL;\n+\t status = NSS_STATUS_TRYAGAIN;\n+\t goto out;\n+\t }\n+\t memcpy (out_addresses, array_begin (&addresses),\n+\t\t addresses_count * sizeof (char *));\n+\t out_addresses[addresses_count] = NULL;\n+\t memcpy (out_aliases, array_begin (&aliases),\n+\t\t aliases_count * sizeof (char *));\n+\t out_aliases[aliases_count] = NULL;\n+\n+\t result->h_addr_list = out_addresses;\n+\t result->h_aliases = out_aliases;\n+\t}\n+\n+ status = NSS_STATUS_SUCCESS;\n+ }\n out:\n scratch_buffer_free (&tmp_buffer);\n+ array_free (&addresses);\n+ array_free (&aliases);\n return status;\n }\n \ndiff --git a/nss/tst-nss-files-hosts-multi.c b/nss/tst-nss-files-hosts-multi.c\nnew file mode 100644\nindex 0000000000..c86a4e05d0\n--- /dev/null\n+++ b/nss/tst-nss-files-hosts-multi.c\n@@ -0,0 +1,327 @@\n+/* Parse /etc/hosts in multi mode with many addresses/aliases.\n+ Copyright (C) 2017 Free Software Foundation, Inc.\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <http://www.gnu.org/licenses/>. */\n+\n+#include <dlfcn.h>\n+#include <errno.h>\n+#include <gnu/lib-names.h>\n+#include <netdb.h>\n+#include <nss.h>\n+#include <stdbool.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <support/check.h>\n+#include <support/check_nss.h>\n+#include <support/namespace.h>\n+#include <support/support.h>\n+#include <support/test-driver.h>\n+#include <support/test-driver.h>\n+#include <support/xmemstream.h>\n+#include <support/xstdio.h>\n+#include <support/xunistd.h>\n+#include <sys/resource.h>\n+\n+struct support_chroot *chroot_env;\n+\n+static void\n+prepare (int argc, char **argv)\n+{\n+ chroot_env = support_chroot_create\n+ ((struct support_chroot_configuration)\n+ {\n+ .resolv_conf = \"\",\n+ .hosts = \"\", /* See write_hosts below. */\n+ .host_conf = \"multi on\\n\",\n+ });\n+}\n+\n+/* Create the /etc/hosts file from outside the chroot. */\n+static void\n+write_hosts (int count)\n+{\n+ TEST_VERIFY (count > 0 && count <= 65535);\n+ FILE *fp = xfopen (chroot_env->path_hosts, \"w\");\n+ fputs (\"127.0.0.1 localhost localhost.localdomain\\n\"\n+ \"::1 localhost localhost.localdomain\\n\",\n+ fp);\n+ for (int i = 0; i < count; ++i)\n+ {\n+ fprintf (fp, \"10.4.%d.%d www4.example.com\\n\",\n+ (i / 256) & 0xff, i & 0xff);\n+ fprintf (fp, \"10.46.%d.%d www.example.com\\n\",\n+ (i / 256) & 0xff, i & 0xff);\n+ fprintf (fp, \"192.0.2.1 alias.example.com v4-%d.example.com\\n\", i);\n+ fprintf (fp, \"2001:db8::6:%x www6.example.com\\n\", i);\n+ fprintf (fp, \"2001:db8::46:%x www.example.com\\n\", i);\n+ fprintf (fp, \"2001:db8::1 alias.example.com v6-%d.example.com\\n\", i);\n+ }\n+ xfclose (fp);\n+}\n+\n+/* Parameters of a single test. */\n+struct test_params\n+{\n+ const char *name; /* Name to query. */\n+ const char *marker; /* Address marker for the name. */\n+ int count; /* Number of addresses/aliases. */\n+ int family; /* AF_INET, AF_INET_6 or AF_UNSPEC. */\n+ bool canonname; /* True if AI_CANONNAME should be enabled. */\n+};\n+\n+/* Expected result of gethostbyname/gethostbyname2. */\n+static char *\n+expected_ghbn (const struct test_params *params)\n+{\n+ TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);\n+\n+ struct xmemstream expected;\n+ xopen_memstream (&expected);\n+ if (strcmp (params->name, \"alias.example.com\") == 0)\n+ {\n+ fprintf (expected.out, \"name: %s\\n\", params->name);\n+ char af;\n+ if (params->family == AF_INET)\n+ af = '4';\n+ else\n+ af = '6';\n+ for (int i = 0; i < params->count; ++i)\n+ fprintf (expected.out, \"alias: v%c-%d.example.com\\n\", af, i);\n+\n+ for (int i = 0; i < params->count; ++i)\n+ if (params->family == AF_INET)\n+ fputs (\"address: 192.0.2.1\\n\", expected.out);\n+ else\n+ fputs (\"address: 2001:db8::1\\n\", expected.out);\n+ }\n+ else /* www/www4/www6 name. */\n+ {\n+ bool do_ipv4 = params->family == AF_INET\n+ && strncmp (params->name, \"www6\", 4) != 0;\n+ bool do_ipv6 = params->family == AF_INET6\n+ && strncmp (params->name, \"www4\", 4) != 0;\n+ if (do_ipv4 || do_ipv6)\n+ {\n+ fprintf (expected.out, \"name: %s\\n\", params->name);\n+ if (do_ipv4)\n+ for (int i = 0; i < params->count; ++i)\n+ fprintf (expected.out, \"address: 10.%s.%d.%d\\n\",\n+ params->marker, i / 256, i % 256);\n+ if (do_ipv6)\n+ for (int i = 0; i < params->count; ++i)\n+ fprintf (expected.out, \"address: 2001:db8::%s:%x\\n\",\n+ params->marker, i);\n+ }\n+ else\n+ fputs (\"error: HOST_NOT_FOUND\\n\", expected.out);\n+ }\n+ xfclose_memstream (&expected);\n+ return expected.buffer;\n+}\n+\n+/* Expected result of getaddrinfo. */\n+static char *\n+expected_gai (const struct test_params *params)\n+{\n+ bool do_ipv4 = false;\n+ bool do_ipv6 = false;\n+ if (params->family == AF_UNSPEC)\n+ do_ipv4 = do_ipv6 = true;\n+ else if (params->family == AF_INET)\n+ do_ipv4 = true;\n+ else if (params->family == AF_INET6)\n+ do_ipv6 = true;\n+\n+ struct xmemstream expected;\n+ xopen_memstream (&expected);\n+ if (strcmp (params->name, \"alias.example.com\") == 0)\n+ {\n+ if (params->canonname)\n+ fprintf (expected.out,\n+ \"flags: AI_CANONNAME\\n\"\n+ \"canonname: %s\\n\",\n+ params->name);\n+\n+ if (do_ipv4)\n+ for (int i = 0; i < params->count; ++i)\n+ fputs (\"address: STREAM/TCP 192.0.2.1 80\\n\", expected.out);\n+ if (do_ipv6)\n+ for (int i = 0; i < params->count; ++i)\n+ fputs (\"address: STREAM/TCP 2001:db8::1 80\\n\", expected.out);\n+ }\n+ else /* www/www4/www6 name. */\n+ {\n+ if (strncmp (params->name, \"www4\", 4) == 0)\n+ do_ipv6 = false;\n+ else if (strncmp (params->name, \"www6\", 4) == 0)\n+ do_ipv4 = false;\n+ /* Otherwise, we have www as the name, so we do both. */\n+\n+ if (do_ipv4 || do_ipv6)\n+ {\n+ if (params->canonname)\n+ fprintf (expected.out,\n+ \"flags: AI_CANONNAME\\n\"\n+ \"canonname: %s\\n\",\n+ params->name);\n+\n+ if (do_ipv4)\n+ for (int i = 0; i < params->count; ++i)\n+ fprintf (expected.out, \"address: STREAM/TCP 10.%s.%d.%d 80\\n\",\n+ params->marker, i / 256, i % 256);\n+ if (do_ipv6)\n+ for (int i = 0; i < params->count; ++i)\n+ fprintf (expected.out,\n+ \"address: STREAM/TCP 2001:db8::%s:%x 80\\n\",\n+ params->marker, i);\n+ }\n+ else\n+ fputs (\"error: Name or service not known\\n\", expected.out);\n+ }\n+ xfclose_memstream (&expected);\n+ return expected.buffer;\n+}\n+\n+static void\n+run_gbhn_gai (struct test_params *params)\n+{\n+ char *ctx = xasprintf (\"name=%s marker=%s count=%d family=%d\",\n+ params->name, params->marker, params->count,\n+ params->family);\n+ if (test_verbose > 0)\n+ printf (\"info: %s\\n\", ctx);\n+\n+ /* Check gethostbyname, gethostbyname2. */\n+ if (params->family == AF_INET)\n+ {\n+ char *expected = expected_ghbn (params);\n+ check_hostent (ctx, gethostbyname (params->name), expected);\n+ free (expected);\n+ }\n+ if (params->family != AF_UNSPEC)\n+ {\n+ char *expected = expected_ghbn (params);\n+ check_hostent (ctx, gethostbyname2 (params->name, params->family),\n+ expected);\n+ free (expected);\n+ }\n+\n+ /* Check getaddrinfo. */\n+ for (int do_canonical = 0; do_canonical < 2; ++do_canonical)\n+ {\n+ params->canonname = do_canonical;\n+ char *expected = expected_gai (params);\n+ struct addrinfo hints =\n+ {\n+ .ai_family = params->family,\n+ .ai_socktype = SOCK_STREAM,\n+ .ai_protocol = IPPROTO_TCP,\n+ };\n+ if (do_canonical)\n+ hints.ai_flags |= AI_CANONNAME;\n+ struct addrinfo *ai;\n+ int ret = getaddrinfo (params->name, \"80\", &hints, &ai);\n+ check_addrinfo (ctx, ai, ret, expected);\n+ if (ret == 0)\n+ freeaddrinfo (ai);\n+ free (expected);\n+ }\n+\n+ free (ctx);\n+}\n+\n+/* Callback for the subprocess which runs the test in a chroot. */\n+static void\n+subprocess (void *closure)\n+{\n+ struct test_params *params = closure;\n+\n+ xchroot (chroot_env->path_chroot);\n+\n+ static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };\n+ static const char *const names[] =\n+ {\n+ \"www.example.com\", \"www4.example.com\", \"www6.example.com\",\n+ \"alias.example.com\",\n+ NULL\n+ };\n+ static const char *const names_marker[] = { \"46\", \"4\", \"6\", \"\" };\n+\n+ for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)\n+ {\n+ params->family = families[family_idx];\n+ for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)\n+ {\n+ params->name = names[names_idx];\n+ params->marker = names_marker[names_idx];\n+ run_gbhn_gai (params);\n+ }\n+ }\n+}\n+\n+/* Run the test for a specific number of addresses/aliases. */\n+static void\n+run_test (int count)\n+{\n+ write_hosts (count);\n+\n+ struct test_params params =\n+ {\n+ .count = count,\n+ };\n+\n+ support_isolate_in_subprocess (subprocess, ¶ms);\n+}\n+\n+static int\n+do_test (void)\n+{\n+ /* This test should not use gigabytes of memory. */\n+ {\n+ struct rlimit limit;\n+ if (getrlimit (RLIMIT_AS, &limit) != 0)\n+ {\n+ printf (\"getrlimit (RLIMIT_AS) failed: %m\\n\");\n+ return 1;\n+ }\n+ long target = 200 * 1024 * 1024;\n+ if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)\n+ {\n+ limit.rlim_cur = target;\n+ if (setrlimit (RLIMIT_AS, &limit) != 0)\n+ {\n+ printf (\"setrlimit (RLIMIT_AS) failed: %m\\n\");\n+ return 1;\n+ }\n+ }\n+ }\n+\n+ __nss_configure_lookup (\"hosts\", \"files\");\n+ if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)\n+ FAIL_EXIT1 (\"could not load \" LIBNSS_DNS_SO \": %s\", dlerror ());\n+\n+ /* Run the tests with a few different address/alias counts. */\n+ for (int count = 1; count <= 111; ++count)\n+ run_test (count);\n+ run_test (1111);\n+ run_test (22222);\n+\n+ support_chroot_free (chroot_env);\n+ return 0;\n+}\n+\n+#define PREPARE prepare\n+#include <support/test-driver.c>\n", "prefixes": [] }