summaryrefslogtreecommitdiff
path: root/nscd/nscd_gethst_r.c
diff options
context:
space:
mode:
Diffstat (limited to 'nscd/nscd_gethst_r.c')
-rw-r--r--nscd/nscd_gethst_r.c143
1 files changed, 84 insertions, 59 deletions
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
index e83e4823ea..03b73a4a47 100644
--- a/nscd/nscd_gethst_r.c
+++ b/nscd/nscd_gethst_r.c
@@ -113,7 +113,6 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
&gc_cycle);
retry:;
- const hst_response_header *hst_resp = NULL;
const char *h_name = NULL;
const uint32_t *aliases_len = NULL;
const char *addr_list = NULL;
@@ -121,18 +120,27 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
int retval = -1;
const char *recend = (const char *) ~UINTMAX_C (0);
int sock = -1;
+ hst_response_header hst_resp;
if (mapped != NO_MAPPING)
{
- const struct datahead *found = __nscd_cache_search (type, key, keylen,
- mapped);
+ /* No const qualifier, as it can change during garbage collection. */
+ struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
if (found != NULL)
{
- hst_resp = &found->data[0].hstdata;
- h_name = (char *) (hst_resp + 1);
- aliases_len = (uint32_t *) (h_name + hst_resp->h_name_len);
+ h_name = (char *) (&found->data[0].hstdata + 1);
+ hst_resp = found->data[0].hstdata;
+ aliases_len = (uint32_t *) (h_name + hst_resp.h_name_len);
addr_list = ((char *) aliases_len
- + hst_resp->h_aliases_cnt * sizeof (uint32_t));
- addr_list_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+ + hst_resp.h_aliases_cnt * sizeof (uint32_t));
+ addr_list_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+ recend = (const char *) found->data + found->recsize;
+ /* Now check if we can trust hst_resp fields. If GC is
+ in progress, it can contain anything. */
+ if (mapped->head->gc_cycle != gc_cycle)
+ {
+ retval = -2;
+ goto out;
+ }
#ifndef _STRING_ARCH_unaligned
/* The aliases_len array in the mapped database might very
@@ -142,51 +150,47 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
!= 0)
{
- uint32_t *tmp = alloca (hst_resp->h_aliases_cnt
+ uint32_t *tmp = alloca (hst_resp.h_aliases_cnt
* sizeof (uint32_t));
aliases_len = memcpy (tmp, aliases_len,
- hst_resp->h_aliases_cnt
+ hst_resp.h_aliases_cnt
* sizeof (uint32_t));
}
#endif
if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
{
- if (hst_resp->h_length == INADDRSZ)
+ if (hst_resp.h_length == INADDRSZ)
addr_list += addr_list_len;
- addr_list_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+ addr_list_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
}
- recend = (const char *) found->data + found->recsize;
if (__builtin_expect ((const char *) addr_list + addr_list_len
> recend, 0))
- goto out_close;
+ goto out;
}
}
- hst_response_header hst_resp_mem;
- if (hst_resp == NULL)
+ if (h_name == NULL)
{
- sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem,
- sizeof (hst_resp_mem));
+ sock = __nscd_open_socket (key, keylen, type, &hst_resp,
+ sizeof (hst_resp));
if (sock == -1)
{
__nss_not_use_nscd_hosts = 1;
goto out;
}
-
- hst_resp = &hst_resp_mem;
}
/* No value found so far. */
*result = NULL;
- if (__builtin_expect (hst_resp->found == -1, 0))
+ if (__builtin_expect (hst_resp.found == -1, 0))
{
/* The daemon does not cache this database. */
__nss_not_use_nscd_hosts = 1;
goto out_close;
}
- if (hst_resp->found == 1)
+ if (hst_resp.found == 1)
{
char *cp = buffer;
uintptr_t align1;
@@ -201,15 +205,15 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
align the pointer and the base of the h_addr_list pointers. */
align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
& (__alignof__ (char *) - 1));
- align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp->h_name_len)
+ align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len)
- ((char *) 0)))
& (__alignof__ (char *) - 1));
- if (buflen < (align1 + hst_resp->h_name_len + align2
- + ((hst_resp->h_aliases_cnt + hst_resp->h_addr_list_cnt
+ if (buflen < (align1 + hst_resp.h_name_len + align2
+ + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt
+ 2)
* sizeof (char *))
- + hst_resp->h_addr_list_cnt * (type == AF_INET
- ? INADDRSZ : IN6ADDRSZ)))
+ + hst_resp.h_addr_list_cnt * (type == AF_INET
+ ? INADDRSZ : IN6ADDRSZ)))
{
no_room:
*h_errnop = NETDB_INTERNAL;
@@ -221,12 +225,12 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
/* Prepare the result as far as we can. */
resultbuf->h_aliases = (char **) cp;
- cp += (hst_resp->h_aliases_cnt + 1) * sizeof (char *);
+ cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *);
resultbuf->h_addr_list = (char **) cp;
- cp += (hst_resp->h_addr_list_cnt + 1) * sizeof (char *);
+ cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *);
resultbuf->h_name = cp;
- cp += hst_resp->h_name_len + align2;
+ cp += hst_resp.h_name_len + align2;
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{
@@ -238,7 +242,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
resultbuf->h_addrtype = AF_INET6;
resultbuf->h_length = IN6ADDRSZ;
}
- for (cnt = 0; cnt < hst_resp->h_addr_list_cnt; ++cnt)
+ for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
{
resultbuf->h_addr_list[cnt] = cp;
cp += resultbuf->h_length;
@@ -250,47 +254,47 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
struct iovec vec[4];
vec[0].iov_base = resultbuf->h_name;
- vec[0].iov_len = hst_resp->h_name_len;
- total_len = hst_resp->h_name_len;
+ vec[0].iov_len = hst_resp.h_name_len;
+ total_len = hst_resp.h_name_len;
n = 1;
- if (hst_resp->h_aliases_cnt > 0)
+ if (hst_resp.h_aliases_cnt > 0)
{
- aliases_len = alloca (hst_resp->h_aliases_cnt
+ aliases_len = alloca (hst_resp.h_aliases_cnt
* sizeof (uint32_t));
vec[n].iov_base = (void *) aliases_len;
- vec[n].iov_len = hst_resp->h_aliases_cnt * sizeof (uint32_t);
+ vec[n].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
- total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
+ total_len += hst_resp.h_aliases_cnt * sizeof (uint32_t);
++n;
}
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{
vec[n].iov_base = resultbuf->h_addr_list[0];
- vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+ vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
- total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
+ total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
++n;
}
else
{
- if (hst_resp->h_length == INADDRSZ)
+ if (hst_resp.h_length == INADDRSZ)
{
- ignore = alloca (hst_resp->h_addr_list_cnt * INADDRSZ);
+ ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
vec[n].iov_base = ignore;
- vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+ vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
- total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
+ total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
++n;
}
vec[n].iov_base = resultbuf->h_addr_list[0];
- vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+ vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
- total_len += hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+ total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
++n;
}
@@ -300,13 +304,13 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
}
else
{
- memcpy (resultbuf->h_name, h_name, hst_resp->h_name_len);
+ memcpy (resultbuf->h_name, h_name, hst_resp.h_name_len);
memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
}
/* Now we also can read the aliases. */
total_len = 0;
- for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
+ for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
{
resultbuf->h_aliases[cnt] = cp;
cp += aliases_len[cnt];
@@ -316,10 +320,25 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
if (__builtin_expect ((const char *) addr_list + addr_list_len
+ total_len > recend, 0))
- goto out_close;
+ {
+ /* aliases_len array might contain garbage during nscd GC cycle,
+ retry rather than fail in that case. */
+ if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+ retval = -2;
+ goto out_close;
+ }
/* See whether this would exceed the buffer capacity. */
if (__builtin_expect (cp > buffer + buflen, 0))
- goto no_room;
+ {
+ /* aliases_len array might contain garbage during nscd GC cycle,
+ retry rather than fail in that case. */
+ if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+ {
+ retval = -2;
+ goto out_close;
+ }
+ goto no_room;
+ }
/* And finally read the aliases. */
if (addr_list == NULL)
@@ -338,14 +357,18 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
(const char *) addr_list + addr_list_len, total_len);
/* Try to detect corrupt databases. */
- if (resultbuf->h_name[hst_resp->h_name_len - 1] != '\0'
- || ({for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
+ if (resultbuf->h_name[hst_resp.h_name_len - 1] != '\0'
+ || ({for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
if (resultbuf->h_aliases[cnt][aliases_len[cnt] - 1]
!= '\0')
break;
- cnt < hst_resp->h_aliases_cnt; }))
- /* We cannot use the database. */
- goto out_close;
+ cnt < hst_resp.h_aliases_cnt; }))
+ {
+ /* We cannot use the database. */
+ if (mapped->head->gc_cycle != gc_cycle)
+ retval = -2;
+ goto out_close;
+ }
retval = 0;
*result = resultbuf;
@@ -354,7 +377,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
else
{
/* Store the error number. */
- *h_errnop = hst_resp->error;
+ *h_errnop = hst_resp.error;
/* The `errno' to some value != ERANGE. */
__set_errno (ENOENT);
@@ -366,19 +389,21 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
if (sock != -1)
close_not_cancel_no_status (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
+ if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
{
/* When we come here this means there has been a GC cycle while we
were looking for the data. This means the data might have been
inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5)
+ if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
{
/* nscd is just running gc now. Disable using the mapping. */
- __nscd_unmap (mapped);
+ if (atomic_decrement_val (&mapped->counter) == 0)
+ __nscd_unmap (mapped);
mapped = NO_MAPPING;
}
- goto retry;
+ if (retval != -1)
+ goto retry;
}
return retval;