diff options
Diffstat (limited to 'nscd/nscd_gethst_r.c')
-rw-r--r-- | nscd/nscd_gethst_r.c | 147 |
1 files changed, 86 insertions, 61 deletions
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c index 516977bcc4..90e1815bdd 100644 --- a/nscd/nscd_gethst_r.c +++ b/nscd/nscd_gethst_r.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1998-2005, 2006 Free Software Foundation, Inc. +/* Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. @@ -118,7 +118,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; @@ -126,18 +125,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 @@ -147,51 +155,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;; + 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) { struct iovec vec[4]; char *cp = buffer; @@ -207,15 +211,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; @@ -227,12 +231,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) { @@ -244,7 +248,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; @@ -254,47 +258,47 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type, if (h_name == NULL) { 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; } @@ -304,13 +308,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]; @@ -320,10 +324,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) @@ -342,14 +361,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; @@ -358,7 +381,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); @@ -370,19 +393,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; |