summaryrefslogtreecommitdiff
path: root/nscd/nscd_helper.c
diff options
context:
space:
mode:
authorAndreas Schwab <schwab@redhat.com>2009-07-29 13:41:25 +0200
committerAndreas Schwab <schwab@redhat.com>2009-07-29 13:41:25 +0200
commitac285df609a4cdd7fe09949bfe0d650a82d9942f (patch)
tree4735e2e6ddc539475f3820d4aea955206fc8c077 /nscd/nscd_helper.c
parent6bdb5f22a0ee205c45d1f465a8b39179830a14cb (diff)
parentc97164f05ba8fa5d2ebf30f1c2de083bc1ead1e1 (diff)
Merge commit 'origin/release/2.10/master' into fedora/2.10/master
Diffstat (limited to 'nscd/nscd_helper.c')
-rw-r--r--nscd/nscd_helper.c42
1 files changed, 32 insertions, 10 deletions
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
index cd3fa24196..fe63f9a7fe 100644
--- a/nscd/nscd_helper.c
+++ b/nscd/nscd_helper.c
@@ -21,6 +21,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
+#include <stddef.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
@@ -467,23 +468,36 @@ __nscd_get_map_ref (request_type type, const char *name,
}
+/* Using sizeof (hashentry) is not always correct to determine the size of
+ the data structure as found in the nscd cache. The program could be
+ a 64-bit process and nscd could be a 32-bit process. In this case
+ sizeof (hashentry) would overestimate the size. The following is
+ the minimum size of such an entry, good enough for our tests here. */
+#define MINIMUM_HASHENTRY_SIZE \
+ (offsetof (struct hashentry, dellist) + sizeof (int32_t))
+
+
/* 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,
- const struct mapped_database *mapped)
+ const struct mapped_database *mapped, size_t datalen)
{
unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
size_t datasize = mapped->datasize;
ref_t trail = mapped->head->array[hash];
+ trail = atomic_forced_read (trail);
ref_t work = trail;
+ size_t loop_cnt = datasize / (MINIMUM_HASHENTRY_SIZE
+ + offsetof (struct datahead, data) / 2);
int tick = 0;
- while (work != ENDREF && work + sizeof (struct hashentry) <= datasize)
+ while (work != ENDREF && work + MINIMUM_HASHENTRY_SIZE <= datasize)
{
struct hashentry *here = (struct hashentry *) (mapped->data + work);
+ ref_t here_key, here_packet;
#ifndef _STRING_ARCH_unaligned
/* Although during garbage collection when moving struct hashentry
@@ -498,13 +512,14 @@ __nscd_cache_search (request_type type, const char *key, size_t keylen,
if (type == here->type
&& keylen == here->len
- && here->key + keylen <= datasize
- && memcmp (key, mapped->data + here->key, keylen) == 0
- && here->packet + sizeof (struct datahead) <= datasize)
+ && (here_key = atomic_forced_read (here->key)) + keylen <= datasize
+ && memcmp (key, mapped->data + here_key, keylen) == 0
+ && ((here_packet = atomic_forced_read (here->packet))
+ + sizeof (struct datahead) <= datasize))
{
/* We found the entry. Increment the appropriate counter. */
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))
@@ -513,14 +528,17 @@ __nscd_cache_search (request_type type, const char *key, size_t keylen,
/* See whether we must ignore the entry or whether something
is wrong because garbage collection is in progress. */
- if (dh->usable && here->packet + dh->allocsize <= datasize)
+ if (dh->usable
+ && here_packet + dh->allocsize <= datasize
+ && (here_packet + offsetof (struct datahead, data) + datalen
+ <= datasize))
return dh;
}
- work = here->next;
+ work = atomic_forced_read (here->next);
/* Prevent endless loops. This should never happen but perhaps
the database got corrupted, accidentally or deliberately. */
- if (work == trail)
+ if (work == trail || loop_cnt-- == 0)
break;
if (tick)
{
@@ -532,7 +550,11 @@ __nscd_cache_search (request_type type, const char *key, size_t keylen,
if ((uintptr_t) trailelem & (__alignof__ (*trailelem) - 1))
return NULL;
#endif
- trail = trailelem->next;
+
+ if (trail + MINIMUM_HASHENTRY_SIZE > datasize)
+ return NULL;
+
+ trail = atomic_forced_read (trailelem->next);
}
tick = 1 - tick;
}