diff options
Diffstat (limited to 'nscd/nscd_gethst_r.c')
-rw-r--r-- | nscd/nscd_gethst_r.c | 143 |
1 files changed, 84 insertions, 59 deletions
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c index e83e482..03b73a4 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; |