diff options
Diffstat (limited to 'nscd/nscd_helper.c')
-rw-r--r-- | nscd/nscd_helper.c | 42 |
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; } |