* nscd/nscd-client.h (__nscd_cache_search): Remove const qualifier

from return value. 
* nscd/nscd_helper.c: Include string.h. 
(__nscd_cache_search): Remove const qualifier from return value. 
On strict alignment architectures check hash entry and data head 
alignment. 
* nscd/nscd_getpw_r.c (nscd_getpw_r): Don't crash or fail because 
mmapped data during GC cycle contains garbage.  If 
__nscd_drop_map_ref fails, decrement mapped->counter when returning 
error or if retrying with NO_MAPPING, only __nscd_unmap if counter 
dropped to 0. 
* nscd/nscd_getgr_r.c (nscd_getgr_r): Likewise. 
* nscd/nscd_initgroups.c (__nscd_getgrouplist): Likewise. 
* nscd/nscd_gethst_r.c (nscd_gethst_r): Likewise. 
* nscd/nscd_getai.c (__nscd_getai): Likewise. 
* nscd/nscd_getserv_r.c (nscd_getserv_r): Likewise.
2007-01-31  Jakub Jelinek  <jakub@redhat.com>

	* nscd/nscd-client.h (__nscd_cache_search): Remove const qualifier
	from return value.
	* nscd/nscd_helper.c: Include string.h.
	(__nscd_cache_search): Remove const qualifier from return value.
	On strict alignment architectures check hash entry and data head
	alignment.
	* nscd/nscd_getpw_r.c (nscd_getpw_r): Don't crash or fail because
	mmapped data during GC cycle contains garbage.  If
	__nscd_drop_map_ref fails, decrement mapped->counter when returning
	error or if retrying with NO_MAPPING, only __nscd_unmap if counter
	dropped to 0.
	* nscd/nscd_getgr_r.c (nscd_getgr_r): Likewise.
	* nscd/nscd_initgroups.c (__nscd_getgrouplist): Likewise.
	* nscd/nscd_gethst_r.c (nscd_gethst_r): Likewise.
	* nscd/nscd_getai.c (__nscd_getai): Likewise.
	* nscd/nscd_getserv_r.c (nscd_getserv_r): Likewise.
This commit is contained in:
Jakub Jelinek 2007-01-31 09:14:21 +00:00
parent ee3142006a
commit 1a77d37f92
9 changed files with 387 additions and 245 deletions

View File

@ -1,3 +1,22 @@
2007-01-31 Jakub Jelinek <jakub@redhat.com>
* nscd/nscd-client.h (__nscd_cache_search): Remove const qualifier
from return value.
* nscd/nscd_helper.c: Include string.h.
(__nscd_cache_search): Remove const qualifier from return value.
On strict alignment architectures check hash entry and data head
alignment.
* nscd/nscd_getpw_r.c (nscd_getpw_r): Don't crash or fail because
mmapped data during GC cycle contains garbage. If
__nscd_drop_map_ref fails, decrement mapped->counter when returning
error or if retrying with NO_MAPPING, only __nscd_unmap if counter
dropped to 0.
* nscd/nscd_getgr_r.c (nscd_getgr_r): Likewise.
* nscd/nscd_initgroups.c (__nscd_getgrouplist): Likewise.
* nscd/nscd_gethst_r.c (nscd_gethst_r): Likewise.
* nscd/nscd_getai.c (__nscd_getai): Likewise.
* nscd/nscd_getserv_r.c (nscd_getserv_r): Likewise.
2007-01-30 Ulrich Drepper <drepper@redhat.com> 2007-01-30 Ulrich Drepper <drepper@redhat.com>
* misc/hsearch_r.c (hdestroy_r): Remove unnecessary test. * misc/hsearch_r.c (hdestroy_r): Remove unnecessary test.

View File

@ -323,10 +323,10 @@ static inline int __nscd_drop_map_ref (struct mapped_database *map,
/* Search the mapped database. */ /* Search the mapped database. */
extern const struct datahead *__nscd_cache_search (request_type type, extern struct datahead *__nscd_cache_search (request_type type,
const char *key, const char *key,
size_t keylen, size_t keylen,
const struct mapped_database *mapped); const struct mapped_database *mapped);
/* Wrappers around read, readv and write that only read/write less than LEN /* Wrappers around read, readv and write that only read/write less than LEN
bytes on error or EOF. */ bytes on error or EOF. */

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. /* Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
@ -42,6 +42,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
{ {
size_t keylen = strlen (key) + 1; size_t keylen = strlen (key) + 1;
int gc_cycle; int gc_cycle;
int nretries = 0;
/* If the mapping is available, try to search there instead of /* If the mapping is available, try to search there instead of
communicating with the nscd. */ communicating with the nscd. */
@ -50,49 +51,53 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
&gc_cycle); &gc_cycle);
retry:; retry:;
const ai_response_header *ai_resp = NULL;
struct nscd_ai_result *resultbuf = NULL; struct nscd_ai_result *resultbuf = NULL;
const char *recend = (const char *) ~UINTMAX_C (0); const char *recend = (const char *) ~UINTMAX_C (0);
char *respdata = NULL; char *respdata = NULL;
int retval = -1; int retval = -1;
int sock = -1; int sock = -1;
ai_response_header ai_resp;
if (mapped != NO_MAPPING) if (mapped != NO_MAPPING)
{ {
const struct datahead *found = __nscd_cache_search (GETAI, key, keylen, struct datahead *found = __nscd_cache_search (GETAI, key, keylen,
mapped); mapped);
if (found != NULL) if (found != NULL)
{ {
ai_resp = &found->data[0].aidata; respdata = (char *) (&found->data[0].aidata + 1);
respdata = (char *) (ai_resp + 1); ai_resp = found->data[0].aidata;
recend = (const char *) found->data + found->recsize; recend = (const char *) found->data + found->recsize;
/* Now check if we can trust ai_resp fields. If GC is
in progress, it can contain anything. */
if (mapped->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
}
} }
} }
/* If we do not have the cache mapped, try to get the data over the /* If we do not have the cache mapped, try to get the data over the
socket. */ socket. */
ai_response_header ai_resp_mem; if (respdata == NULL)
if (ai_resp == NULL)
{ {
sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp_mem, sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp,
sizeof (ai_resp_mem)); sizeof (ai_resp));
if (sock == -1) if (sock == -1)
{ {
/* nscd not running or wrong version. */ /* nscd not running or wrong version. */
__nss_not_use_nscd_hosts = 1; __nss_not_use_nscd_hosts = 1;
goto out; goto out;
} }
ai_resp = &ai_resp_mem;
} }
if (ai_resp->found == 1) if (ai_resp.found == 1)
{ {
size_t datalen = ai_resp->naddrs + ai_resp->addrslen + ai_resp->canonlen; size_t datalen = ai_resp.naddrs + ai_resp.addrslen + ai_resp.canonlen;
/* This check is really only affects the case where the data /* This check really only affects the case where the data
comes from the mapped cache. */ comes from the mapped cache. */
if ((char *) (ai_resp + 1) + datalen > recend) if (respdata + datalen > recend)
{ {
assert (sock == -1); assert (sock == -1);
goto out; goto out;
@ -108,10 +113,10 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
} }
/* Set up the data structure, including pointers. */ /* Set up the data structure, including pointers. */
resultbuf->naddrs = ai_resp->naddrs; resultbuf->naddrs = ai_resp.naddrs;
resultbuf->addrs = (char *) (resultbuf + 1); resultbuf->addrs = (char *) (resultbuf + 1);
resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp->addrslen); resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp.addrslen);
if (ai_resp->canonlen != 0) if (ai_resp.canonlen != 0)
resultbuf->canon = (char *) (resultbuf->family + resultbuf->naddrs); resultbuf->canon = (char *) (resultbuf->family + resultbuf->naddrs);
else else
resultbuf->canon = NULL; resultbuf->canon = NULL;
@ -137,10 +142,13 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
/* Try to detect corrupt databases. */ /* Try to detect corrupt databases. */
if (resultbuf->canon != NULL if (resultbuf->canon != NULL
&& resultbuf->canon[ai_resp->canonlen - 1] != '\0') && resultbuf->canon[ai_resp.canonlen - 1] != '\0')
/* We cannot use the database. */ /* We cannot use the database. */
{ {
free (resultbuf); if (mapped->head->gc_cycle != gc_cycle)
retval = -2;
else
free (resultbuf);
goto out_close; goto out_close;
} }
@ -150,7 +158,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
} }
else else
{ {
if (__builtin_expect (ai_resp->found == -1, 0)) if (__builtin_expect (ai_resp.found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_hosts = 1; __nss_not_use_nscd_hosts = 1;
@ -158,7 +166,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
} }
/* Store the error number. */ /* Store the error number. */
*h_errnop = ai_resp->error; *h_errnop = ai_resp.error;
/* The `errno' to some value != ERANGE. */ /* The `errno' to some value != ERANGE. */
__set_errno (ENOENT); __set_errno (ENOENT);
@ -170,22 +178,25 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
if (sock != -1) if (sock != -1)
close_not_cancel_no_status (sock); close_not_cancel_no_status (sock);
out: 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 /* 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 were looking for the data. This means the data might have been
inconsistent. Retry if possible. */ inconsistent. Retry if possible. */
if ((gc_cycle & 1) != 0) if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
{ {
/* nscd is just running gc now. Disable using the mapping. */ /* 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; mapped = NO_MAPPING;
} }
*result = NULL; if (retval != -1)
free (resultbuf); {
*result = NULL;
goto retry; free (resultbuf);
goto retry;
}
} }
return retval; return retval;

View File

@ -1,4 +1,5 @@
/* Copyright (C) 1998-2000, 2002-2005, 2006 Free Software Foundation, Inc. /* Copyright (C) 1998-2000, 2002-2005, 2006, 2007
Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998. Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
@ -88,6 +89,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
struct group **result) struct group **result)
{ {
int gc_cycle; int gc_cycle;
int nretries = 0;
const uint32_t *len = NULL; const uint32_t *len = NULL;
size_t lensize = 0; size_t lensize = 0;
@ -97,55 +99,59 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
&__gr_map_handle, &__gr_map_handle,
&gc_cycle); &gc_cycle);
retry:; retry:;
const gr_response_header *gr_resp = NULL;
const char *gr_name = NULL; const char *gr_name = NULL;
size_t gr_name_len = 0; size_t gr_name_len = 0;
int retval = -1; int retval = -1;
const char *recend = (const char *) ~UINTMAX_C (0); const char *recend = (const char *) ~UINTMAX_C (0);
gr_response_header gr_resp;
if (mapped != NO_MAPPING) if (mapped != NO_MAPPING)
{ {
const struct datahead *found = __nscd_cache_search (type, key, keylen, struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
mapped);
if (found != NULL) if (found != NULL)
{ {
gr_resp = &found->data[0].grdata; len = (const uint32_t *) (&found->data[0].grdata + 1);
len = (const uint32_t *) (gr_resp + 1); gr_resp = found->data[0].grdata;
/* The alignment is always sufficient. */
assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
gr_name = ((const char *) len gr_name = ((const char *) len
+ gr_resp->gr_mem_cnt * sizeof (uint32_t)); + gr_resp.gr_mem_cnt * sizeof (uint32_t));
gr_name_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len; gr_name_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
recend = (const char *) found->data + found->recsize; recend = (const char *) found->data + found->recsize;
/* Now check if we can trust gr_resp fields. If GC is
in progress, it can contain anything. */
if (mapped->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
}
/* The alignment is always sufficient, unless GC is in progress. */
assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
} }
} }
gr_response_header gr_resp_mem;
int sock = -1; int sock = -1;
if (gr_resp == NULL) if (gr_name == NULL)
{ {
sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem, sock = __nscd_open_socket (key, keylen, type, &gr_resp,
sizeof (gr_resp_mem)); sizeof (gr_resp));
if (sock == -1) if (sock == -1)
{ {
__nss_not_use_nscd_group = 1; __nss_not_use_nscd_group = 1;
goto out; goto out;
} }
gr_resp = &gr_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
*result = NULL; *result = NULL;
if (__builtin_expect (gr_resp->found == -1, 0)) if (__builtin_expect (gr_resp.found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_group = 1; __nss_not_use_nscd_group = 1;
goto out_close; goto out_close;
} }
if (gr_resp->found == 1) if (gr_resp.found == 1)
{ {
struct iovec vec[2]; struct iovec vec[2];
char *p = buffer; char *p = buffer;
@ -157,8 +163,8 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
align the pointer. */ align the pointer. */
align = ((__alignof__ (char *) - (p - ((char *) 0))) align = ((__alignof__ (char *) - (p - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
total_len = (align + (1 + gr_resp->gr_mem_cnt) * sizeof (char *) total_len = (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
+ gr_resp->gr_name_len + gr_resp->gr_passwd_len); + gr_resp.gr_name_len + gr_resp.gr_passwd_len);
if (__builtin_expect (buflen < total_len, 0)) if (__builtin_expect (buflen < total_len, 0))
{ {
no_room: no_room:
@ -170,16 +176,16 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
p += align; p += align;
resultbuf->gr_mem = (char **) p; resultbuf->gr_mem = (char **) p;
p += (1 + gr_resp->gr_mem_cnt) * sizeof (char *); p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
/* Set pointers for strings. */ /* Set pointers for strings. */
resultbuf->gr_name = p; resultbuf->gr_name = p;
p += gr_resp->gr_name_len; p += gr_resp.gr_name_len;
resultbuf->gr_passwd = p; resultbuf->gr_passwd = p;
p += gr_resp->gr_passwd_len; p += gr_resp.gr_passwd_len;
/* Fill in what we know now. */ /* Fill in what we know now. */
resultbuf->gr_gid = gr_resp->gr_gid; resultbuf->gr_gid = gr_resp.gr_gid;
/* Read the length information, group name, and password. */ /* Read the length information, group name, and password. */
if (gr_name == NULL) if (gr_name == NULL)
@ -187,17 +193,17 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
/* Allocate array to store lengths. */ /* Allocate array to store lengths. */
if (lensize == 0) if (lensize == 0)
{ {
lensize = gr_resp->gr_mem_cnt * sizeof (uint32_t); lensize = gr_resp.gr_mem_cnt * sizeof (uint32_t);
len = (uint32_t *) alloca (lensize); len = (uint32_t *) alloca (lensize);
} }
else if (gr_resp->gr_mem_cnt * sizeof (uint32_t) > lensize) else if (gr_resp.gr_mem_cnt * sizeof (uint32_t) > lensize)
len = extend_alloca (len, lensize, len = extend_alloca (len, lensize,
gr_resp->gr_mem_cnt * sizeof (uint32_t)); gr_resp.gr_mem_cnt * sizeof (uint32_t));
vec[0].iov_base = (void *) len; vec[0].iov_base = (void *) len;
vec[0].iov_len = gr_resp->gr_mem_cnt * sizeof (uint32_t); vec[0].iov_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
vec[1].iov_base = resultbuf->gr_name; vec[1].iov_base = resultbuf->gr_name;
vec[1].iov_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len; vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
total_len = vec[0].iov_len + vec[1].iov_len; total_len = vec[0].iov_len + vec[1].iov_len;
/* Get this data. */ /* Get this data. */
@ -209,14 +215,14 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
/* We already have the data. Just copy the group name and /* We already have the data. Just copy the group name and
password. */ password. */
memcpy (resultbuf->gr_name, gr_name, memcpy (resultbuf->gr_name, gr_name,
gr_resp->gr_name_len + gr_resp->gr_passwd_len); gr_resp.gr_name_len + gr_resp.gr_passwd_len);
/* Clear the terminating entry. */ /* Clear the terminating entry. */
resultbuf->gr_mem[gr_resp->gr_mem_cnt] = NULL; resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
/* Prepare reading the group members. */ /* Prepare reading the group members. */
total_len = 0; total_len = 0;
for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt) for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
{ {
resultbuf->gr_mem[cnt] = p; resultbuf->gr_mem[cnt] = p;
total_len += len[cnt]; total_len += len[cnt];
@ -224,9 +230,25 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
} }
if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0)) if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
goto out_close; {
/* len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
if (__builtin_expect (total_len > buflen, 0)) if (__builtin_expect (total_len > buflen, 0))
goto no_room; {
/* len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out_close;
}
else
goto no_room;
}
retval = 0; retval = 0;
if (gr_name == NULL) if (gr_name == NULL)
@ -248,14 +270,14 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
/* Try to detect corrupt databases. */ /* Try to detect corrupt databases. */
if (resultbuf->gr_name[gr_name_len - 1] != '\0' if (resultbuf->gr_name[gr_name_len - 1] != '\0'
|| resultbuf->gr_passwd[gr_resp->gr_passwd_len - 1] != '\0' || resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0'
|| ({for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt) || ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0') if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0')
break; break;
cnt < gr_resp->gr_mem_cnt; })) cnt < gr_resp.gr_mem_cnt; }))
{ {
/* We cannot use the database. */ /* We cannot use the database. */
retval = -1; retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
goto out_close; goto out_close;
} }
@ -274,19 +296,21 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
if (sock != -1) if (sock != -1)
close_not_cancel_no_status (sock); close_not_cancel_no_status (sock);
out: 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 /* 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 were looking for the data. This means the data might have been
inconsistent. Retry if possible. */ inconsistent. Retry if possible. */
if ((gc_cycle & 1) != 0) if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
{ {
/* nscd is just running gc now. Disable using the mapping. */ /* 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; mapped = NO_MAPPING;
} }
goto retry; if (retval != -1)
goto retry;
} }
return retval; return retval;

View File

@ -113,7 +113,6 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
&gc_cycle); &gc_cycle);
retry:; retry:;
const hst_response_header *hst_resp = NULL;
const char *h_name = NULL; const char *h_name = NULL;
const uint32_t *aliases_len = NULL; const uint32_t *aliases_len = NULL;
const char *addr_list = 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; int retval = -1;
const char *recend = (const char *) ~UINTMAX_C (0); const char *recend = (const char *) ~UINTMAX_C (0);
int sock = -1; int sock = -1;
hst_response_header hst_resp;
if (mapped != NO_MAPPING) if (mapped != NO_MAPPING)
{ {
const struct datahead *found = __nscd_cache_search (type, key, keylen, /* No const qualifier, as it can change during garbage collection. */
mapped); struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
if (found != NULL) if (found != NULL)
{ {
hst_resp = &found->data[0].hstdata; h_name = (char *) (&found->data[0].hstdata + 1);
h_name = (char *) (hst_resp + 1); hst_resp = found->data[0].hstdata;
aliases_len = (uint32_t *) (h_name + hst_resp->h_name_len); aliases_len = (uint32_t *) (h_name + hst_resp.h_name_len);
addr_list = ((char *) aliases_len addr_list = ((char *) aliases_len
+ hst_resp->h_aliases_cnt * sizeof (uint32_t)); + hst_resp.h_aliases_cnt * sizeof (uint32_t));
addr_list_len = hst_resp->h_addr_list_cnt * INADDRSZ; 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 #ifndef _STRING_ARCH_unaligned
/* The aliases_len array in the mapped database might very /* 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)) if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
!= 0) != 0)
{ {
uint32_t *tmp = alloca (hst_resp->h_aliases_cnt uint32_t *tmp = alloca (hst_resp.h_aliases_cnt
* sizeof (uint32_t)); * sizeof (uint32_t));
aliases_len = memcpy (tmp, aliases_len, aliases_len = memcpy (tmp, aliases_len,
hst_resp->h_aliases_cnt hst_resp.h_aliases_cnt
* sizeof (uint32_t)); * sizeof (uint32_t));
} }
#endif #endif
if (type != GETHOSTBYADDR && type != GETHOSTBYNAME) if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
{ {
if (hst_resp->h_length == INADDRSZ) if (hst_resp.h_length == INADDRSZ)
addr_list += addr_list_len; 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 if (__builtin_expect ((const char *) addr_list + addr_list_len
> recend, 0)) > recend, 0))
goto out_close; goto out;
} }
} }
hst_response_header hst_resp_mem; if (h_name == NULL)
if (hst_resp == NULL)
{ {
sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem, sock = __nscd_open_socket (key, keylen, type, &hst_resp,
sizeof (hst_resp_mem)); sizeof (hst_resp));
if (sock == -1) if (sock == -1)
{ {
__nss_not_use_nscd_hosts = 1; __nss_not_use_nscd_hosts = 1;
goto out; goto out;
} }
hst_resp = &hst_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
*result = NULL; *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. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_hosts = 1; __nss_not_use_nscd_hosts = 1;
goto out_close; goto out_close;
} }
if (hst_resp->found == 1) if (hst_resp.found == 1)
{ {
char *cp = buffer; char *cp = buffer;
uintptr_t align1; 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. */ align the pointer and the base of the h_addr_list pointers. */
align1 = ((__alignof__ (char *) - (cp - ((char *) 0))) align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__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))) - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
if (buflen < (align1 + hst_resp->h_name_len + align2 if (buflen < (align1 + hst_resp.h_name_len + align2
+ ((hst_resp->h_aliases_cnt + hst_resp->h_addr_list_cnt + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt
+ 2) + 2)
* sizeof (char *)) * sizeof (char *))
+ hst_resp->h_addr_list_cnt * (type == AF_INET + hst_resp.h_addr_list_cnt * (type == AF_INET
? INADDRSZ : IN6ADDRSZ))) ? INADDRSZ : IN6ADDRSZ)))
{ {
no_room: no_room:
*h_errnop = NETDB_INTERNAL; *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. */ /* Prepare the result as far as we can. */
resultbuf->h_aliases = (char **) cp; 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; 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; resultbuf->h_name = cp;
cp += hst_resp->h_name_len + align2; cp += hst_resp.h_name_len + align2;
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME) 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_addrtype = AF_INET6;
resultbuf->h_length = IN6ADDRSZ; 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; resultbuf->h_addr_list[cnt] = cp;
cp += resultbuf->h_length; cp += resultbuf->h_length;
@ -250,47 +254,47 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
struct iovec vec[4]; struct iovec vec[4];
vec[0].iov_base = resultbuf->h_name; vec[0].iov_base = resultbuf->h_name;
vec[0].iov_len = hst_resp->h_name_len; vec[0].iov_len = hst_resp.h_name_len;
total_len = hst_resp->h_name_len; total_len = hst_resp.h_name_len;
n = 1; 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)); * sizeof (uint32_t));
vec[n].iov_base = (void *) aliases_len; 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; ++n;
} }
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME) if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{ {
vec[n].iov_base = resultbuf->h_addr_list[0]; 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; ++n;
} }
else 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_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; ++n;
} }
vec[n].iov_base = resultbuf->h_addr_list[0]; 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; ++n;
} }
@ -300,13 +304,13 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
} }
else 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); memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
} }
/* Now we also can read the aliases. */ /* Now we also can read the aliases. */
total_len = 0; 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; resultbuf->h_aliases[cnt] = cp;
cp += aliases_len[cnt]; 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 if (__builtin_expect ((const char *) addr_list + addr_list_len
+ total_len > recend, 0)) + 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. */ /* See whether this would exceed the buffer capacity. */
if (__builtin_expect (cp > buffer + buflen, 0)) 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. */ /* And finally read the aliases. */
if (addr_list == NULL) 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); (const char *) addr_list + addr_list_len, total_len);
/* Try to detect corrupt databases. */ /* Try to detect corrupt databases. */
if (resultbuf->h_name[hst_resp->h_name_len - 1] != '\0' if (resultbuf->h_name[hst_resp.h_name_len - 1] != '\0'
|| ({for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt) || ({for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
if (resultbuf->h_aliases[cnt][aliases_len[cnt] - 1] if (resultbuf->h_aliases[cnt][aliases_len[cnt] - 1]
!= '\0') != '\0')
break; break;
cnt < hst_resp->h_aliases_cnt; })) cnt < hst_resp.h_aliases_cnt; }))
/* We cannot use the database. */ {
goto out_close; /* We cannot use the database. */
if (mapped->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
retval = 0; retval = 0;
*result = resultbuf; *result = resultbuf;
@ -354,7 +377,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
else else
{ {
/* Store the error number. */ /* Store the error number. */
*h_errnop = hst_resp->error; *h_errnop = hst_resp.error;
/* The `errno' to some value != ERANGE. */ /* The `errno' to some value != ERANGE. */
__set_errno (ENOENT); __set_errno (ENOENT);
@ -366,19 +389,21 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
if (sock != -1) if (sock != -1)
close_not_cancel_no_status (sock); close_not_cancel_no_status (sock);
out: 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 /* 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 were looking for the data. This means the data might have been
inconsistent. Retry if possible. */ 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 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; mapped = NO_MAPPING;
} }
goto retry; if (retval != -1)
goto retry;
} }
return retval; return retval;

View File

@ -1,4 +1,5 @@
/* Copyright (C) 1998, 1999, 2003, 2004, 2005 Free Software Foundation, Inc. /* Copyright (C) 1998, 1999, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998. Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
@ -88,76 +89,81 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
struct passwd **result) struct passwd **result)
{ {
int gc_cycle; int gc_cycle;
int nretries = 0;
/* If the mapping is available, try to search there instead of /* If the mapping is available, try to search there instead of
communicating with the nscd. */ communicating with the nscd. */
struct mapped_database *mapped; struct mapped_database *mapped;
mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle); mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle);
retry:; retry:;
const pw_response_header *pw_resp = NULL;
const char *pw_name = NULL; const char *pw_name = NULL;
int retval = -1; int retval = -1;
const char *recend = (const char *) ~UINTMAX_C (0); const char *recend = (const char *) ~UINTMAX_C (0);
pw_response_header pw_resp;
if (mapped != NO_MAPPING) if (mapped != NO_MAPPING)
{ {
const struct datahead *found = __nscd_cache_search (type, key, keylen, struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
mapped);
if (found != NULL) if (found != NULL)
{ {
pw_resp = &found->data[0].pwdata; pw_name = (const char *) (&found->data[0].pwdata + 1);
pw_name = (const char *) (pw_resp + 1); pw_resp = found->data[0].pwdata;
recend = (const char *) found->data + found->recsize; recend = (const char *) found->data + found->recsize;
/* Now check if we can trust pw_resp fields. If GC is
in progress, it can contain anything. */
if (mapped->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
}
} }
} }
pw_response_header pw_resp_mem;
int sock = -1; int sock = -1;
if (pw_resp == NULL) if (pw_name == NULL)
{ {
sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem, sock = __nscd_open_socket (key, keylen, type, &pw_resp,
sizeof (pw_resp_mem)); sizeof (pw_resp));
if (sock == -1) if (sock == -1)
{ {
__nss_not_use_nscd_passwd = 1; __nss_not_use_nscd_passwd = 1;
goto out; goto out;
} }
pw_resp = &pw_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
*result = NULL; *result = NULL;
if (__builtin_expect (pw_resp->found == -1, 0)) if (__builtin_expect (pw_resp.found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_passwd = 1; __nss_not_use_nscd_passwd = 1;
goto out_close; goto out_close;
} }
if (pw_resp->found == 1) if (pw_resp.found == 1)
{ {
/* Set the information we already have. */ /* Set the information we already have. */
resultbuf->pw_uid = pw_resp->pw_uid; resultbuf->pw_uid = pw_resp.pw_uid;
resultbuf->pw_gid = pw_resp->pw_gid; resultbuf->pw_gid = pw_resp.pw_gid;
char *p = buffer; char *p = buffer;
/* get pw_name */ /* get pw_name */
resultbuf->pw_name = p; resultbuf->pw_name = p;
p += pw_resp->pw_name_len; p += pw_resp.pw_name_len;
/* get pw_passwd */ /* get pw_passwd */
resultbuf->pw_passwd = p; resultbuf->pw_passwd = p;
p += pw_resp->pw_passwd_len; p += pw_resp.pw_passwd_len;
/* get pw_gecos */ /* get pw_gecos */
resultbuf->pw_gecos = p; resultbuf->pw_gecos = p;
p += pw_resp->pw_gecos_len; p += pw_resp.pw_gecos_len;
/* get pw_dir */ /* get pw_dir */
resultbuf->pw_dir = p; resultbuf->pw_dir = p;
p += pw_resp->pw_dir_len; p += pw_resp.pw_dir_len;
/* get pw_pshell */ /* get pw_pshell */
resultbuf->pw_shell = p; resultbuf->pw_shell = p;
p += pw_resp->pw_shell_len; p += pw_resp.pw_shell_len;
ssize_t total = p - buffer; ssize_t total = p - buffer;
if (__builtin_expect (pw_name + total > recend, 0)) if (__builtin_expect (pw_name + total > recend, 0))
@ -189,14 +195,14 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
memcpy (resultbuf->pw_name, pw_name, total); memcpy (resultbuf->pw_name, pw_name, total);
/* Try to detect corrupt databases. */ /* Try to detect corrupt databases. */
if (resultbuf->pw_name[pw_resp->pw_name_len - 1] != '\0' if (resultbuf->pw_name[pw_resp.pw_name_len - 1] != '\0'
|| resultbuf->pw_passwd[pw_resp->pw_passwd_len - 1] != '\0' || resultbuf->pw_passwd[pw_resp.pw_passwd_len - 1] != '\0'
|| resultbuf->pw_gecos[pw_resp->pw_gecos_len - 1] != '\0' || resultbuf->pw_gecos[pw_resp.pw_gecos_len - 1] != '\0'
|| resultbuf->pw_dir[pw_resp->pw_dir_len - 1] != '\0' || resultbuf->pw_dir[pw_resp.pw_dir_len - 1] != '\0'
|| resultbuf->pw_shell[pw_resp->pw_shell_len - 1] != '\0') || resultbuf->pw_shell[pw_resp.pw_shell_len - 1] != '\0')
{ {
/* We cannot use the database. */ /* We cannot use the database. */
retval = -1; retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
goto out_close; goto out_close;
} }
@ -215,19 +221,21 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
if (sock != -1) if (sock != -1)
close_not_cancel_no_status (sock); close_not_cancel_no_status (sock);
out: 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 /* 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 were looking for the data. This means the data might have been
inconsistent. Retry if possible. */ inconsistent. Retry if possible. */
if ((gc_cycle & 1) != 0) if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
{ {
/* nscd is just running gc now. Disable using the mapping. */ /* 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; mapped = NO_MAPPING;
} }
goto retry; if (retval != -1)
goto retry;
} }
return retval; return retval;

View File

@ -93,7 +93,6 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
"/", 1), proto ?: "", protolen + 1); "/", 1), proto ?: "", protolen + 1);
retry:; retry:;
const serv_response_header *serv_resp = NULL;
const char *s_name = NULL; const char *s_name = NULL;
const char *s_proto = NULL; const char *s_proto = NULL;
const uint32_t *aliases_len = NULL; const uint32_t *aliases_len = NULL;
@ -101,19 +100,28 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
int retval = -1; int retval = -1;
const char *recend = (const char *) ~UINTMAX_C (0); const char *recend = (const char *) ~UINTMAX_C (0);
int sock = -1; int sock = -1;
serv_response_header serv_resp;
if (mapped != NO_MAPPING) if (mapped != NO_MAPPING)
{ {
const struct datahead *found = __nscd_cache_search (type, key, keylen, struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
mapped);
if (found != NULL) if (found != NULL)
{ {
serv_resp = &found->data[0].servdata; s_name = (char *) (&found->data[0].servdata + 1);
s_name = (char *) (serv_resp + 1); serv_resp = found->data[0].servdata;
s_proto = s_name + serv_resp->s_name_len; s_proto = s_name + serv_resp.s_name_len;
aliases_len = (uint32_t *) (s_proto + serv_resp->s_proto_len); aliases_len = (uint32_t *) (s_proto + serv_resp.s_proto_len);
aliases_list = ((char *) aliases_len aliases_list = ((char *) aliases_len
+ serv_resp->s_aliases_cnt * sizeof (uint32_t)); + serv_resp.s_aliases_cnt * sizeof (uint32_t));
recend = (const char *) found->data + found->recsize;
/* Now check if we can trust serv_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 #ifndef _STRING_ARCH_unaligned
/* The aliases_len array in the mapped database might very /* The aliases_len array in the mapped database might very
@ -123,46 +131,42 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1)) if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
!= 0) != 0)
{ {
uint32_t *tmp = alloca (serv_resp->s_aliases_cnt uint32_t *tmp = alloca (serv_resp.s_aliases_cnt
* sizeof (uint32_t)); * sizeof (uint32_t));
aliases_len = memcpy (tmp, aliases_len, aliases_len = memcpy (tmp, aliases_len,
serv_resp->s_aliases_cnt serv_resp.s_aliases_cnt
* sizeof (uint32_t)); * sizeof (uint32_t));
} }
#endif #endif
recend = (const char *) found->data + found->recsize;
if (__builtin_expect ((const char *) aliases_len if (__builtin_expect ((const char *) aliases_len
+ serv_resp->s_aliases_cnt * sizeof (uint32_t) + serv_resp.s_aliases_cnt * sizeof (uint32_t)
> recend, 0)) > recend, 0))
goto out_close; goto out;
} }
} }
serv_response_header serv_resp_mem; if (s_name == NULL)
if (serv_resp == NULL)
{ {
sock = __nscd_open_socket (key, keylen, type, &serv_resp_mem, sock = __nscd_open_socket (key, keylen, type, &serv_resp,
sizeof (serv_resp_mem)); sizeof (serv_resp));
if (sock == -1) if (sock == -1)
{ {
__nss_not_use_nscd_services = 1; __nss_not_use_nscd_services = 1;
goto out; goto out;
} }
serv_resp = &serv_resp_mem;
} }
/* No value found so far. */ /* No value found so far. */
*result = NULL; *result = NULL;
if (__builtin_expect (serv_resp->found == -1, 0)) if (__builtin_expect (serv_resp.found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_services = 1; __nss_not_use_nscd_services = 1;
goto out_close; goto out_close;
} }
if (serv_resp->found == 1) if (serv_resp.found == 1)
{ {
char *cp = buf; char *cp = buf;
uintptr_t align1; uintptr_t align1;
@ -176,13 +180,13 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
align the pointer and the base of the h_addr_list pointers. */ align the pointer and the base of the h_addr_list pointers. */
align1 = ((__alignof__ (char *) - (cp - ((char *) 0))) align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
align2 = ((__alignof__ (char *) - ((cp + align1 + serv_resp->s_name_len align2 = ((__alignof__ (char *) - ((cp + align1 + serv_resp.s_name_len
+ serv_resp->s_proto_len) + serv_resp.s_proto_len)
- ((char *) 0))) - ((char *) 0)))
& (__alignof__ (char *) - 1)); & (__alignof__ (char *) - 1));
if (buflen < (align1 + serv_resp->s_name_len + serv_resp->s_proto_len if (buflen < (align1 + serv_resp.s_name_len + serv_resp.s_proto_len
+ align2 + align2
+ (serv_resp->s_aliases_cnt + 1) * sizeof (char *))) + (serv_resp.s_aliases_cnt + 1) * sizeof (char *)))
{ {
no_room: no_room:
__set_errno (ERANGE); __set_errno (ERANGE);
@ -193,31 +197,31 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
/* Prepare the result as far as we can. */ /* Prepare the result as far as we can. */
resultbuf->s_aliases = (char **) cp; resultbuf->s_aliases = (char **) cp;
cp += (serv_resp->s_aliases_cnt + 1) * sizeof (char *); cp += (serv_resp.s_aliases_cnt + 1) * sizeof (char *);
resultbuf->s_name = cp; resultbuf->s_name = cp;
cp += serv_resp->s_name_len; cp += serv_resp.s_name_len;
resultbuf->s_proto = cp; resultbuf->s_proto = cp;
cp += serv_resp->s_proto_len + align2; cp += serv_resp.s_proto_len + align2;
resultbuf->s_port = serv_resp->s_port; resultbuf->s_port = serv_resp.s_port;
if (s_name == NULL) if (s_name == NULL)
{ {
struct iovec vec[2]; struct iovec vec[2];
vec[0].iov_base = resultbuf->s_name; vec[0].iov_base = resultbuf->s_name;
vec[0].iov_len = serv_resp->s_name_len + serv_resp->s_proto_len; vec[0].iov_len = serv_resp.s_name_len + serv_resp.s_proto_len;
total_len = vec[0].iov_len; total_len = vec[0].iov_len;
n = 1; n = 1;
if (serv_resp->s_aliases_cnt > 0) if (serv_resp.s_aliases_cnt > 0)
{ {
aliases_len = alloca (serv_resp->s_aliases_cnt aliases_len = alloca (serv_resp.s_aliases_cnt
* sizeof (uint32_t)); * sizeof (uint32_t));
vec[n].iov_base = (void *) aliases_len; vec[n].iov_base = (void *) aliases_len;
vec[n].iov_len = serv_resp->s_aliases_cnt * sizeof (uint32_t); vec[n].iov_len = serv_resp.s_aliases_cnt * sizeof (uint32_t);
total_len += serv_resp->s_aliases_cnt * sizeof (uint32_t); total_len += serv_resp.s_aliases_cnt * sizeof (uint32_t);
++n; ++n;
} }
@ -226,11 +230,11 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
} }
else else
memcpy (resultbuf->s_name, s_name, memcpy (resultbuf->s_name, s_name,
serv_resp->s_name_len + serv_resp->s_proto_len); serv_resp.s_name_len + serv_resp.s_proto_len);
/* Now we also can read the aliases. */ /* Now we also can read the aliases. */
total_len = 0; total_len = 0;
for (cnt = 0; cnt < serv_resp->s_aliases_cnt; ++cnt) for (cnt = 0; cnt < serv_resp.s_aliases_cnt; ++cnt)
{ {
resultbuf->s_aliases[cnt] = cp; resultbuf->s_aliases[cnt] = cp;
cp += aliases_len[cnt]; cp += aliases_len[cnt];
@ -240,10 +244,26 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
if (__builtin_expect ((const char *) aliases_list + total_len > recend, if (__builtin_expect ((const char *) aliases_list + total_len > recend,
0)) 0))
goto out_close; {
/* aliases_len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
/* See whether this would exceed the buffer capacity. */ /* See whether this would exceed the buffer capacity. */
if (__builtin_expect (cp > buf + buflen, 0)) if (__builtin_expect (cp > buf + buflen, 0))
goto no_room; {
/* aliases_len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out_close;
}
goto no_room;
}
/* And finally read the aliases. */ /* And finally read the aliases. */
if (aliases_list == NULL) if (aliases_list == NULL)
@ -261,15 +281,19 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
memcpy (resultbuf->s_aliases[0], aliases_list, total_len); memcpy (resultbuf->s_aliases[0], aliases_list, total_len);
/* Try to detect corrupt databases. */ /* Try to detect corrupt databases. */
if (resultbuf->s_name[serv_resp->s_name_len - 1] != '\0' if (resultbuf->s_name[serv_resp.s_name_len - 1] != '\0'
|| resultbuf->s_proto[serv_resp->s_proto_len - 1] != '\0' || resultbuf->s_proto[serv_resp.s_proto_len - 1] != '\0'
|| ({for (cnt = 0; cnt < serv_resp->s_aliases_cnt; ++cnt) || ({for (cnt = 0; cnt < serv_resp.s_aliases_cnt; ++cnt)
if (resultbuf->s_aliases[cnt][aliases_len[cnt] - 1] if (resultbuf->s_aliases[cnt][aliases_len[cnt] - 1]
!= '\0') != '\0')
break; break;
cnt < serv_resp->s_aliases_cnt; })) cnt < serv_resp.s_aliases_cnt; }))
/* We cannot use the database. */ {
goto out_close; /* We cannot use the database. */
if (mapped->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
retval = 0; retval = 0;
*result = resultbuf; *result = resultbuf;
@ -287,19 +311,21 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
if (sock != -1) if (sock != -1)
close_not_cancel_no_status (sock); close_not_cancel_no_status (sock);
out: 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 /* 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 were looking for the data. This means the data might have been
inconsistent. Retry if possible. */ 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 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; mapped = NO_MAPPING;
} }
goto retry; if (retval != -1)
goto retry;
} }
return retval; return retval;

View File

@ -1,4 +1,5 @@
/* Copyright (C) 1998-2002,2003,2004,2005,2006 Free Software Foundation, Inc. /* Copyright (C) 1998-2002,2003,2004,2005,2006,2007
Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
@ -21,6 +22,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -362,7 +364,10 @@ __nscd_get_map_ref (request_type type, const char *name,
} }
const struct datahead * /* Don't return const struct datahead *, as eventhough the record
is normally constant, it can change arbitrarily during nscd
garbage collection. */
struct datahead *
__nscd_cache_search (request_type type, const char *key, size_t keylen, __nscd_cache_search (request_type type, const char *key, size_t keylen,
const struct mapped_database *mapped) const struct mapped_database *mapped)
{ {
@ -374,16 +379,32 @@ __nscd_cache_search (request_type type, const char *key, size_t keylen,
{ {
struct hashentry *here = (struct hashentry *) (mapped->data + work); struct hashentry *here = (struct hashentry *) (mapped->data + work);
#ifndef _STRING_ARCH_unaligned
/* Although during garbage collection when moving struct hashentry
records around we first copy from old to new location and then
adjust pointer from previous hashentry to it, there is no barrier
between those memory writes. It is very unlikely to hit it,
so check alignment only if a misaligned load can crash the
application. */
if ((uintptr_t) here & (__alignof__ (*here) - 1))
return NULL;
#endif
if (type == here->type if (type == here->type
&& keylen == here->len && keylen == here->len
&& here->key + here->len <= datasize && here->key + keylen <= datasize
&& memcmp (key, mapped->data + here->key, keylen) == 0 && memcmp (key, mapped->data + here->key, keylen) == 0
&& here->packet + sizeof (struct datahead) <= datasize) && here->packet + sizeof (struct datahead) <= datasize)
{ {
/* We found the entry. Increment the appropriate counter. */ /* We found the entry. Increment the appropriate counter. */
const struct datahead *dh struct datahead *dh
= (struct datahead *) (mapped->data + here->packet); = (struct datahead *) (mapped->data + here->packet);
#ifndef _STRING_ARCH_unaligned
if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
return NULL;
#endif
/* See whether we must ignore the entry or whether something /* See whether we must ignore the entry or whether something
is wrong because garbage collection is in progress. */ is wrong because garbage collection is in progress. */
if (dh->usable && here->packet + dh->allocsize <= datasize) if (dh->usable && here->packet + dh->allocsize <= datasize)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. /* Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
@ -39,6 +39,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
{ {
size_t userlen = strlen (user) + 1; size_t userlen = strlen (user) + 1;
int gc_cycle; int gc_cycle;
int nretries = 0;
/* If the mapping is available, try to search there instead of /* If the mapping is available, try to search there instead of
communicating with the nscd. */ communicating with the nscd. */
@ -46,44 +47,49 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle); mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle);
retry:; retry:;
const initgr_response_header *initgr_resp = NULL;
char *respdata = NULL; char *respdata = NULL;
int retval = -1; int retval = -1;
int sock = -1; int sock = -1;
initgr_response_header initgr_resp;
if (mapped != NO_MAPPING) if (mapped != NO_MAPPING)
{ {
const struct datahead *found = __nscd_cache_search (INITGROUPS, user, struct datahead *found = __nscd_cache_search (INITGROUPS, user,
userlen, mapped); userlen, mapped);
if (found != NULL) if (found != NULL)
{ {
initgr_resp = &found->data[0].initgrdata; respdata = (char *) (&found->data[0].initgrdata + 1);
respdata = (char *) (initgr_resp + 1); initgr_resp = found->data[0].initgrdata;
char *recend = (char *) found->data + found->recsize; char *recend = (char *) found->data + found->recsize;
if (respdata + initgr_resp->ngrps * sizeof (int32_t) > recend) /* Now check if we can trust initgr_resp fields. If GC is
in progress, it can contain anything. */
if (mapped->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
}
if (respdata + initgr_resp.ngrps * sizeof (int32_t) > recend)
goto out; goto out;
} }
} }
/* If we do not have the cache mapped, try to get the data over the /* If we do not have the cache mapped, try to get the data over the
socket. */ socket. */
initgr_response_header initgr_resp_mem; if (respdata == NULL)
if (initgr_resp == NULL)
{ {
sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp_mem, sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp,
sizeof (initgr_resp_mem)); sizeof (initgr_resp));
if (sock == -1) if (sock == -1)
{ {
/* nscd not running or wrong version. */ /* nscd not running or wrong version. */
__nss_not_use_nscd_group = 1; __nss_not_use_nscd_group = 1;
goto out; goto out;
} }
initgr_resp = &initgr_resp_mem;
} }
if (initgr_resp->found == 1) if (initgr_resp.found == 1)
{ {
/* The following code assumes that gid_t and int32_t are the /* The following code assumes that gid_t and int32_t are the
same size. This is the case for al existing implementation. same size. This is the case for al existing implementation.
@ -91,40 +97,40 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
doesn't use memcpy but instead copies each array element one doesn't use memcpy but instead copies each array element one
by one. */ by one. */
assert (sizeof (int32_t) == sizeof (gid_t)); assert (sizeof (int32_t) == sizeof (gid_t));
assert (initgr_resp->ngrps >= 0); assert (initgr_resp.ngrps >= 0);
/* Make sure we have enough room. We always count GROUP in even /* Make sure we have enough room. We always count GROUP in even
though we might not end up adding it. */ though we might not end up adding it. */
if (*size < initgr_resp->ngrps + 1) if (*size < initgr_resp.ngrps + 1)
{ {
gid_t *newp = realloc (*groupsp, gid_t *newp = realloc (*groupsp,
(initgr_resp->ngrps + 1) * sizeof (gid_t)); (initgr_resp.ngrps + 1) * sizeof (gid_t));
if (newp == NULL) if (newp == NULL)
/* We cannot increase the buffer size. */ /* We cannot increase the buffer size. */
goto out_close; goto out_close;
*groupsp = newp; *groupsp = newp;
*size = initgr_resp->ngrps + 1; *size = initgr_resp.ngrps + 1;
} }
if (respdata == NULL) if (respdata == NULL)
{ {
/* Read the data from the socket. */ /* Read the data from the socket. */
if ((size_t) __readall (sock, *groupsp, initgr_resp->ngrps if ((size_t) __readall (sock, *groupsp, initgr_resp.ngrps
* sizeof (gid_t)) * sizeof (gid_t))
== initgr_resp->ngrps * sizeof (gid_t)) == initgr_resp.ngrps * sizeof (gid_t))
retval = initgr_resp->ngrps; retval = initgr_resp.ngrps;
} }
else else
{ {
/* Just copy the data. */ /* Just copy the data. */
retval = initgr_resp->ngrps; retval = initgr_resp.ngrps;
memcpy (*groupsp, respdata, retval * sizeof (gid_t)); memcpy (*groupsp, respdata, retval * sizeof (gid_t));
} }
} }
else else
{ {
if (__builtin_expect (initgr_resp->found == -1, 0)) if (__builtin_expect (initgr_resp.found == -1, 0))
{ {
/* The daemon does not cache this database. */ /* The daemon does not cache this database. */
__nss_not_use_nscd_group = 1; __nss_not_use_nscd_group = 1;
@ -153,19 +159,21 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
if (sock != -1) if (sock != -1)
close_not_cancel_no_status (sock); close_not_cancel_no_status (sock);
out: 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 /* 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 were looking for the data. This means the data might have been
inconsistent. Retry if possible. */ inconsistent. Retry if possible. */
if ((gc_cycle & 1) != 0) if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
{ {
/* nscd is just running gc now. Disable using the mapping. */ /* 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; mapped = NO_MAPPING;
} }
goto retry; if (retval != -1)
goto retry;
} }
return retval; return retval;