diff -rup a/include/alloca.h b/include/alloca.h --- a/include/alloca.h 2012-02-29 13:11:19.439693476 -0700 +++ b/include/alloca.h 2012-02-29 13:11:49.832530623 -0700 @@ -49,15 +49,24 @@ libc_hidden_proto (__libc_alloca_cutoff) #if defined stackinfo_get_sp && defined stackinfo_sub_sp # define alloca_account(size, avar) \ - ({ void *old__ = stackinfo_get_sp (); \ - void *m__ = __alloca (size); \ - avar += stackinfo_sub_sp (old__); \ + ({ void *old__ = stackinfo_get_sp (); \ + void *m__ = __alloca (size); \ + avar += stackinfo_sub_sp (old__); \ + m__; }) +# define extend_alloca_account(buf, len, newlen, avar) \ + ({ void *old__ = stackinfo_get_sp (); \ + void *m__ = extend_alloca (buf, len, newlen); \ + avar += stackinfo_sub_sp (old__); \ m__; }) #else # define alloca_account(size, avar) \ - ({ size_t s__ = (size); \ - avar += s__; \ + ({ size_t s__ = (size); \ + avar += s__; \ __alloca (s__); }) +# define extend_alloca_account(buf, len, newlen, avar) \ + ({ size_t s__ = (newlen); \ + avar += s__; \ + extend_alloca (buf, len, s__); }) #endif #endif diff -rup a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c --- a/sysdeps/posix/getaddrinfo.c 2012-02-29 13:11:19.588692676 -0700 +++ b/sysdeps/posix/getaddrinfo.c 2012-02-29 13:12:42.972245862 -0700 @@ -278,6 +278,7 @@ gaih_inet (const char *name, const struc bool got_ipv6 = false; const char *canon = NULL; const char *orig_name = name; + size_t alloca_used = 0; if (req->ai_protocol || req->ai_socktype) { @@ -310,7 +311,7 @@ gaih_inet (const char *name, const struc if (tp->name[0]) { st = (struct gaih_servtuple *) - __alloca (sizeof (struct gaih_servtuple)); + alloca_account (sizeof (struct gaih_servtuple), alloca_used); if ((rc = gaih_inet_serv (service->name, tp, req, st))) return rc; @@ -334,7 +335,8 @@ gaih_inet (const char *name, const struc continue; newp = (struct gaih_servtuple *) - __alloca (sizeof (struct gaih_servtuple)); + alloca_account (sizeof (struct gaih_servtuple), + alloca_used); if ((rc = gaih_inet_serv (service->name, tp, req, newp))) { @@ -362,7 +364,7 @@ gaih_inet (const char *name, const struc if (req->ai_socktype || req->ai_protocol) { - st = __alloca (sizeof (struct gaih_servtuple)); + st = alloca_account (sizeof (struct gaih_servtuple), alloca_used); st->next = NULL; st->socktype = tp->socktype; st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) @@ -379,7 +381,8 @@ gaih_inet (const char *name, const struc { struct gaih_servtuple *newp; - newp = __alloca (sizeof (struct gaih_servtuple)); + newp = alloca_account (sizeof (struct gaih_servtuple), + alloca_used); newp->next = NULL; newp->socktype = tp->socktype; newp->protocol = tp->protocol; @@ -391,10 +394,17 @@ gaih_inet (const char *name, const struc } } + bool malloc_name = false; + bool malloc_addrmem = false; + struct gaih_addrtuple *addrmem = NULL; + bool malloc_canonbuf = false; + char *canonbuf = NULL; + bool malloc_tmpbuf = false; + char *tmpbuf = NULL; + int result = 0; if (name != NULL) { - at = __alloca (sizeof (struct gaih_addrtuple)); - + at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); at->family = AF_UNSPEC; at->scopeid = 0; at->next = NULL; @@ -412,6 +422,7 @@ gaih_inet (const char *name, const struc rc = __idna_to_ascii_lz (name, &p, idn_flags); if (rc != IDNA_SUCCESS) { + /* No need to jump to free_and_return here. */ if (rc == IDNA_MALLOC_ERROR) return -EAI_MEMORY; if (rc == IDNA_DLOPEN_ERROR) @@ -421,10 +432,7 @@ gaih_inet (const char *name, const struc /* In case the output string is the same as the input string no new string has been allocated. */ if (p != name) - { - name = strdupa (p); - free (p); - } + malloc_name = true; } #endif @@ -441,23 +449,59 @@ gaih_inet (const char *name, const struc at->family = AF_INET6; } else - return -EAI_ADDRFAMILY; + { + result = -EAI_ADDRFAMILY; + goto free_and_return; + } if (req->ai_flags & AI_CANONNAME) canon = name; } else if (at->family == AF_UNSPEC) { - char *namebuf = (char *) name; char *scope_delim = strchr (name, SCOPE_DELIMITER); + int e; - if (__builtin_expect (scope_delim != NULL, 0)) - { - namebuf = alloca (scope_delim - name + 1); - *((char *) __mempcpy (namebuf, name, scope_delim - name)) = '\0'; - } + { + bool malloc_namebuf = false; + char *namebuf = (char *) name; + + if (__builtin_expect (scope_delim != NULL, 0)) + { + if (malloc_name) + *scope_delim = '\0'; + else + { + if (__libc_use_alloca (alloca_used + + scope_delim - name + 1)) + { + namebuf = alloca_account (scope_delim - name + 1, + alloca_used); + *((char *) __mempcpy (namebuf, name, + scope_delim - name)) = '\0'; + } + else + { + namebuf = strndup (name, scope_delim - name); + if (namebuf == NULL) + { + assert (!malloc_name); + return -EAI_MEMORY; + } + malloc_namebuf = true; + } + } + } - if (inet_pton (AF_INET6, namebuf, at->addr) > 0) + e = inet_pton (AF_INET6, namebuf, at->addr); + + if (malloc_namebuf) + free (namebuf); + else if (scope_delim != NULL && malloc_name) + /* Undo what we did above. */ + *scope_delim = SCOPE_DELIMITER; + } + if (e > 0) { if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) at->family = AF_INET6; @@ -468,7 +512,10 @@ gaih_inet (const char *name, const struc at->family = AF_INET; } else - return -EAI_ADDRFAMILY; + { + result = -EAI_ADDRFAMILY; + goto free_and_return; + } if (scope_delim != NULL) { @@ -490,7 +537,10 @@ gaih_inet (const char *name, const struc at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end, 10); if (*end != '\0') - return GAIH_OKIFUNSPEC | -EAI_NONAME; + { + result = GAIH_OKIFUNSPEC | -EAI_NONAME; + goto free_and_return; + } } } @@ -520,59 +570,80 @@ gaih_inet (const char *name, const struc { int family = req->ai_family; size_t tmpbuflen = 512; - char *tmpbuf = alloca (tmpbuflen); + assert (tmpbuf == NULL); + tmpbuf = alloca_account (tmpbuflen, alloca_used); int rc; struct hostent th; struct hostent *h; int herrno; - simple_again: while (1) { - rc = __gethostbyname2_r (name, family, &th, tmpbuf, + rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf, tmpbuflen, &h, &herrno); if (rc != ERANGE || herrno != NETDB_INTERNAL) break; - tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); + + if (!malloc_tmpbuf + && __libc_use_alloca (alloca_used + 2 * tmpbuflen)) + tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, + 2 * tmpbuflen, + alloca_used); + else + { + char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL, + 2 * tmpbuflen); + if (newp == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } + tmpbuf = newp; + malloc_tmpbuf = true; + tmpbuflen = 2 * tmpbuflen; + } } if (rc == 0) { - if (h == NULL) + if (h != NULL) { - if (req->ai_family == AF_INET6 - && (req->ai_flags & AI_V4MAPPED) - && family == AF_INET6) + int i; + /* We found data, count the number of addresses. */ + for (i = 0; h->h_addr_list[i]; ++i) + ; + if (i > 0 && *pat != NULL) + --i; + + if (__libc_use_alloca (alloca_used + + i * sizeof (struct gaih_addrtuple))) + addrmem = alloca_account (i * sizeof (struct gaih_addrtuple), + alloca_used); + else { - /* Try again, this time looking for IPv4 - addresses. */ - family = AF_INET; - goto simple_again; + addrmem = malloc (i + * sizeof (struct gaih_addrtuple)); + if (addrmem == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; } + malloc_addrmem = true; } - else - { - /* We found data, now convert it into the list. */ - for (int i = 0; h->h_addr_list[i]; ++i) + + /* Now convert it into the list. */ + struct gaih_addrtuple *addrfree = addrmem; + for (i = 0; h->h_addr_list[i]; ++i) { if (*pat == NULL) { - *pat = __alloca (sizeof (struct gaih_addrtuple)); + *pat = addrfree++; (*pat)->scopeid = 0; } (*pat)->next = NULL; - (*pat)->family = req->ai_family; - if (family == req->ai_family) + (*pat)->family = AF_INET; memcpy ((*pat)->addr, h->h_addr_list[i], h->h_length); - else - { - uint32_t *addr = (uint32_t *) (*pat)->addr; - addr[3] = *(uint32_t *) h->h_addr_list[i]; - addr[2] = htonl (0xffff); - addr[1] = 0; - addr[0] = 0; - } pat = &((*pat)->next); } } @@ -582,15 +653,16 @@ gaih_inet (const char *name, const struc if (herrno == NETDB_INTERNAL) { __set_h_errno (herrno); - return -EAI_SYSTEM; - } - if (herrno == TRY_AGAIN) - { - return -EAI_AGAIN; + result = -EAI_SYSTEM; } + else if (herrno == TRY_AGAIN) + result = -EAI_AGAIN; + else /* We made requests but they turned out no data. The name is known, though. */ - return GAIH_OKIFUNSPEC | -EAI_NODATA; + result = GAIH_OKIFUNSPEC | -EAI_NODATA; + + goto free_and_return; } goto process_list; @@ -613,21 +685,56 @@ gaih_inet (const char *name, const struc bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; char *addrs = air->addrs; + if (__libc_use_alloca (alloca_used + + air->naddrs * sizeof (struct gaih_addrtuple))) + addrmem = alloca_account (air->naddrs + * sizeof (struct gaih_addrtuple), + alloca_used); + else + { + addrmem = malloc (air->naddrs + * sizeof (struct gaih_addrtuple)); + if (addrmem == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } + malloc_addrmem = true; + } + + struct gaih_addrtuple *addrfree = addrmem; for (int i = 0; i < air->naddrs; ++i) { socklen_t size = (air->family[i] == AF_INET ? INADDRSZ : IN6ADDRSZ); if (*pat == NULL) { - *pat = __alloca (sizeof (struct gaih_addrtuple)); + *pat = addrfree++; (*pat)->scopeid = 0; } uint32_t *pataddr = (*pat)->addr; (*pat)->next = NULL; if (added_canon || air->canon == NULL) (*pat)->name = NULL; - else - canon = (*pat)->name = strdupa (air->canon); + else if (canonbuf == NULL) + { + size_t canonlen = strlen (air->canon) + 1; + if ((req->ai_flags & AI_CANONIDN) != 0 + && __libc_use_alloca (alloca_used + canonlen)) + canonbuf = alloca_account (canonlen, alloca_used); + else + { + canonbuf = malloc (canonlen); + if (canonbuf == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } + malloc_canonbuf = true; + } + canon = (*pat)->name = memcpy (canonbuf, air->canon, + canonlen); + } if (air->family[i] == AF_INET && req->ai_family == AF_INET6 @@ -657,20 +764,26 @@ gaih_inet (const char *name, const struc free (air); if (at->family == AF_UNSPEC) - return GAIH_OKIFUNSPEC | -EAI_NONAME; + { + result = GAIH_OKIFUNSPEC | -EAI_NONAME; + goto free_and_return; + } goto process_list; } else if (err == 0) /* The database contains a negative entry. */ - return 0; + goto free_and_return; else if (__nss_not_use_nscd_hosts == 0) { if (herrno == NETDB_INTERNAL && errno == ENOMEM) - return -EAI_MEMORY; - if (herrno == TRY_AGAIN) - return -EAI_AGAIN; - return -EAI_SYSTEM; + result = -EAI_MEMORY; + else if (herrno == TRY_AGAIN) + result = -EAI_AGAIN; + else + result = -EAI_SYSTEM; + + goto free_and_return; } } #endif @@ -699,7 +812,19 @@ gaih_inet (const char *name, const struc _res.options &= ~RES_USE_INET6; size_t tmpbuflen = 1024; - char *tmpbuf = alloca (tmpbuflen); + malloc_tmpbuf = !__libc_use_alloca (alloca_used + tmpbuflen); + assert (tmpbuf == NULL); + if (!malloc_tmpbuf) + tmpbuf = alloca_account (tmpbuflen, alloca_used); + else + { + tmpbuf = malloc (tmpbuflen); + if (tmpbuf == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } + } while (!no_more) { @@ -728,8 +853,25 @@ gaih_inet (const char *name, const struc no_data = herrno == NO_DATA; break; } - tmpbuf = extend_alloca (tmpbuf, - tmpbuflen, 2 * tmpbuflen); + + if (!malloc_tmpbuf + && __libc_use_alloca (alloca_used + 2 * tmpbuflen)) + tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, + 2 * tmpbuflen, + alloca_used); + else + { + char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL, + 2 * tmpbuflen); + if (newp == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } + tmpbuf = newp; + malloc_tmpbuf = true; + tmpbuflen = 2 * tmpbuflen; + } } if (status == NSS_STATUS_SUCCESS) @@ -832,18 +974,40 @@ gaih_inet (const char *name, const struc if (cfct != NULL) { const size_t max_fqdn_len = 256; - char *buf = alloca (max_fqdn_len); + if ((req->ai_flags & AI_CANONIDN) != 0 + && __libc_use_alloca (alloca_used + + max_fqdn_len)) + canonbuf = alloca_account (max_fqdn_len, + alloca_used); + else + { + canonbuf = malloc (max_fqdn_len); + if (canonbuf == NULL) + { + result = -EAI_MEMORY; + goto free_and_return; + } + malloc_canonbuf = true; + } char *s; if (DL_CALL_FCT (cfct, (at->name ?: name, - buf, max_fqdn_len, + canonbuf, + max_fqdn_len, &s, &rc, &herrno)) == NSS_STATUS_SUCCESS) canon = s; else - /* Set to name now to avoid using - gethostbyaddr. */ - canon = name; + { + /* Set to name now to avoid using + gethostbyaddr. */ + if (malloc_canonbuf) + { + free (canonbuf); + malloc_canonbuf = false; + } + canon = name; + } } } status = NSS_STATUS_SUCCESS; @@ -878,22 +1042,27 @@ gaih_inet (const char *name, const struc { /* If both requests timed out report this. */ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) - return -EAI_AGAIN; + result = -EAI_AGAIN; + else + /* We made requests but they turned out no data. The name + is known, though. */ + result = GAIH_OKIFUNSPEC | -EAI_NODATA; - /* We made requests but they turned out no data. The name - is known, though. */ - return GAIH_OKIFUNSPEC | -EAI_NODATA; + goto free_and_return; } } process_list: if (at->family == AF_UNSPEC) - return GAIH_OKIFUNSPEC | -EAI_NONAME; + { + result = GAIH_OKIFUNSPEC | -EAI_NONAME; + goto free_and_return; + } } else { struct gaih_addrtuple *atr; - atr = at = __alloca (sizeof (struct gaih_addrtuple)); + atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); memset (at, '\0', sizeof (struct gaih_addrtuple)); if (req->ai_family == AF_UNSPEC) @@ -932,6 +1101,9 @@ gaih_inet (const char *name, const struc /* Only the first entry gets the canonical name. */ if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0) { + char *tmpbuf2 = NULL; + bool malloc_tmpbuf2 = false; + if (canon == NULL) { /* If the canonical name cannot be determined, use @@ -952,11 +1124,16 @@ gaih_inet (const char *name, const struc int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags); if (rc != IDNA_SUCCESS) { + if (malloc_tmpbuf2) + free (tmpbuf2); + if (rc == IDNA_MALLOC_ERROR) - return -EAI_MEMORY; - if (rc == IDNA_DLOPEN_ERROR) - return -EAI_SYSTEM; - return -EAI_IDN_ENCODE; + result = -EAI_MEMORY; + else if (rc == IDNA_DLOPEN_ERROR) + result = -EAI_SYSTEM; + else + result = -EAI_IDN_ENCODE; + goto free_and_return; } /* In case the output string is the same as the input string no new string has been allocated and we @@ -970,10 +1147,25 @@ gaih_inet (const char *name, const struc #ifdef HAVE_LIBIDN make_copy: #endif - canon = strdup (canon); - if (canon == NULL) - return -EAI_MEMORY; + if (malloc_canonbuf) + /* We already allocated the string using malloc. */ + malloc_canonbuf = false; + else + { + canon = strdup (canon); + if (canon == NULL) + { + if (malloc_tmpbuf2) + free (tmpbuf2); + + result = -EAI_MEMORY; + goto free_and_return; + } + } } + + if (malloc_tmpbuf2) + free (tmpbuf2); } family = at2->family; @@ -999,7 +1191,8 @@ gaih_inet (const char *name, const struc if (ai == NULL) { free ((char *) canon); - return -EAI_MEMORY; + result = -EAI_MEMORY; + goto free_and_return; } ai->ai_flags = req->ai_flags; @@ -1052,7 +1245,18 @@ gaih_inet (const char *name, const struc at2 = at2->next; } } - return 0; + + free_and_return: + if (malloc_name) + free ((char *) name); + if (malloc_addrmem) + free (addrmem); + if (malloc_canonbuf) + free (canonbuf); + if (malloc_tmpbuf) + free (tmpbuf); + + return result; }