From adeed29a99dcd0efdbfe4dbd5da74e7b01966c67 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 20 Feb 2017 16:09:54 +0100 Subject: [PATCH] Use SipHash hash function to mitigate HashDos attempts. This change attempts to switch to an hash function which mitigates the effects of the HashDoS attack (denial of service attack trying to force data structures to worst case behavior) while at the same time providing Redis with an hash function that does not expect the input data to be word aligned, a condition no longer true now that sds.c strings have a varialbe length header. Note that it is possible sometimes that even using an hash function for which collisions cannot be generated without knowing the seed, special implementation details or the exposure of the seed in an indirect way (for example the ability to add elements to a Set and check the return in which Redis returns them with SMEMBERS) may make the attacker's life simpler in the process of trying to guess the correct seed, however the next step would be to switch to a log(N) data structure when too many items in a single bucket are detected: this seems like an overkill in the case of Redis. SPEED REGRESION TESTS: In order to verify that switching from MurmurHash to SipHash had no impact on speed, a set of benchmarks involving fast insertion of 5 million of keys were performed. The result shows Redis with SipHash in high pipelining conditions to be about 4% slower compared to using the previous hash function. However this could partially be related to the fact that the current implementation does not attempt to hash whole words at a time but reads single bytes, in order to have an output which is endian-netural and at the same time working on systems where unaligned memory accesses are a problem. Further X86 specific optimizations should be tested, the function may easily get at the same level of MurMurHash2 if a few optimizations are performed. --- src/Makefile | 2 +- src/config.c | 2 +- src/debug.c | 2 - src/dict.c | 75 ++--------- src/dict.h | 10 +- src/latency.c | 2 +- src/module.c | 2 +- src/sentinel.c | 2 +- src/server.c | 14 ++- src/server.h | 2 +- src/siphash.c | 328 +++++++++++++++++++++++++++++++++++++++++++++++++ src/t_zset.c | 2 +- 12 files changed, 361 insertions(+), 82 deletions(-) create mode 100644 src/siphash.c diff --git a/src/Makefile b/src/Makefile index 3f445f40..5f7536e8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -128,7 +128,7 @@ endif REDIS_SERVER_NAME=redis-server REDIS_SENTINEL_NAME=redis-sentinel -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o REDIS_CLI_NAME=redis-cli REDIS_CLI_OBJ=anet.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o REDIS_BENCHMARK_NAME=redis-benchmark diff --git a/src/config.c b/src/config.c index 83651877..900274f6 100644 --- a/src/config.c +++ b/src/config.c @@ -1423,7 +1423,7 @@ void configGetCommand(client *c) { /* We use the following dictionary type to store where a configuration * option is mentioned in the old configuration file, so it's * like "maxmemory" -> list of line numbers (first line is zero). */ -unsigned int dictSdsCaseHash(const void *key); +uint64_t dictSdsCaseHash(const void *key); int dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2); void dictSdsDestructor(void *privdata, void *val); void dictListDestructor(void *privdata, void *val); diff --git a/src/debug.c b/src/debug.c index a6bc62dc..a4caa49f 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1029,8 +1029,6 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) { /* Log INFO and CLIENT LIST */ serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n"); infostring = genRedisInfoString("all"); - infostring = sdscatprintf(infostring, "hash_init_value: %u\n", - dictGetHashFunctionSeed()); serverLogRaw(LL_WARNING|LL_RAW, infostring); serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n"); clients = getAllClientsInfoString(); diff --git a/src/dict.c b/src/dict.c index 59aef772..8ce73596 100644 --- a/src/dict.c +++ b/src/dict.c @@ -37,11 +37,11 @@ #include #include +#include #include #include #include #include -#include #include "dict.h" #include "zmalloc.h" @@ -71,77 +71,28 @@ static int _dictInit(dict *ht, dictType *type, void *privDataPtr); /* -------------------------- hash functions -------------------------------- */ -static uint32_t dict_hash_function_seed = 5381; +static uint8_t dict_hash_function_seed[16]; -void dictSetHashFunctionSeed(uint32_t seed) { - dict_hash_function_seed = seed; +void dictSetHashFunctionSeed(uint8_t *seed) { + memcpy(dict_hash_function_seed,seed,sizeof(dict_hash_function_seed)); } -uint32_t dictGetHashFunctionSeed(void) { +uint8_t *dictGetHashFunctionSeed(void) { return dict_hash_function_seed; } -/* MurmurHash2, by Austin Appleby - * Note - This code makes a few assumptions about how your machine behaves - - * 1. We can read a 4-byte value from any address without crashing - * 2. sizeof(int) == 4 - * - * And it has a few limitations - - * - * 1. It will not work incrementally. - * 2. It will not produce the same results on little-endian and big-endian - * machines. - */ -unsigned int dictGenHashFunction(const void *key, int len) { - /* 'm' and 'r' are mixing constants generated offline. - They're not really 'magic', they just happen to work well. */ - uint32_t seed = dict_hash_function_seed; - const uint32_t m = 0x5bd1e995; - const int r = 24; +/* The default hashing function uses SipHash implementation + * in siphash.c. */ - /* Initialize the hash to a 'random' value */ - uint32_t h = seed ^ len; +uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k); +uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k); - /* Mix 4 bytes at a time into the hash */ - const unsigned char *data = (const unsigned char *)key; - - while(len >= 4) { - uint32_t k = *(uint32_t*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - /* Handle the last few bytes of the input array */ - switch(len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; h *= m; - }; - - /* Do a few final mixes of the hash to ensure the last few - * bytes are well-incorporated. */ - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return (unsigned int)h; +uint64_t dictGenHashFunction(const void *key, int len) { + return siphash(key,len,dict_hash_function_seed); } -/* And a case insensitive hash function (based on djb hash) */ -unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) { - unsigned int hash = (unsigned int)dict_hash_function_seed; - - while (len--) - hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */ - return hash; +uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) { + return siphash_nocase(buf,len,dict_hash_function_seed); } /* ----------------------------- API implementation ------------------------- */ diff --git a/src/dict.h b/src/dict.h index 60a423a2..bf316a00 100644 --- a/src/dict.h +++ b/src/dict.h @@ -56,7 +56,7 @@ typedef struct dictEntry { } dictEntry; typedef struct dictType { - unsigned int (*hashFunction)(const void *key); + uint64_t (*hashFunction)(const void *key); void *(*keyDup)(void *privdata, const void *key); void *(*valDup)(void *privdata, const void *obj); int (*keyCompare)(void *privdata, const void *key1, const void *key2); @@ -168,15 +168,15 @@ void dictReleaseIterator(dictIterator *iter); dictEntry *dictGetRandomKey(dict *d); unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count); void dictGetStats(char *buf, size_t bufsize, dict *d); -unsigned int dictGenHashFunction(const void *key, int len); -unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); +uint64_t dictGenHashFunction(const void *key, int len); +uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len); void dictEmpty(dict *d, void(callback)(void*)); void dictEnableResize(void); void dictDisableResize(void); int dictRehash(dict *d, int n); int dictRehashMilliseconds(dict *d, int ms); -void dictSetHashFunctionSeed(unsigned int initval); -unsigned int dictGetHashFunctionSeed(void); +void dictSetHashFunctionSeed(uint8_t *seed); +uint8_t *dictGetHashFunctionSeed(void); unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanBucketFunction *bucketfn, void *privdata); unsigned int dictGetHash(dict *d, const void *key); dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, unsigned int hash); diff --git a/src/latency.c b/src/latency.c index 53e0ec7b..9e9f1f13 100644 --- a/src/latency.c +++ b/src/latency.c @@ -41,7 +41,7 @@ int dictStringKeyCompare(void *privdata, const void *key1, const void *key2) { return strcmp(key1,key2) == 0; } -unsigned int dictStringHash(const void *key) { +uint64_t dictStringHash(const void *key) { return dictGenHashFunction(key, strlen(key)); } diff --git a/src/module.c b/src/module.c index 1fbc5094..3b90eae4 100644 --- a/src/module.c +++ b/src/module.c @@ -3264,7 +3264,7 @@ void *RM_GetBlockedClientPrivateData(RedisModuleCtx *ctx) { /* server.moduleapi dictionary type. Only uses plain C strings since * this gets queries from modules. */ -unsigned int dictCStringKeyHash(const void *key) { +uint64_t dictCStringKeyHash(const void *key) { return dictGenHashFunction((unsigned char*)key, strlen((char*)key)); } diff --git a/src/sentinel.c b/src/sentinel.c index 1f47dd33..6c6a3a0c 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -379,7 +379,7 @@ void sentinelSimFailureCrash(void); /* ========================= Dictionary types =============================== */ -unsigned int dictSdsHash(const void *key); +uint64_t dictSdsHash(const void *key); int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); void releaseSentinelRedisInstance(sentinelRedisInstance *ri); diff --git a/src/server.c b/src/server.c index 8bf6510d..0494a4e7 100644 --- a/src/server.c +++ b/src/server.c @@ -482,16 +482,16 @@ int dictObjKeyCompare(void *privdata, const void *key1, return dictSdsKeyCompare(privdata,o1->ptr,o2->ptr); } -unsigned int dictObjHash(const void *key) { +uint64_t dictObjHash(const void *key) { const robj *o = key; return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr)); } -unsigned int dictSdsHash(const void *key) { +uint64_t dictSdsHash(const void *key) { return dictGenHashFunction((unsigned char*)key, sdslen((char*)key)); } -unsigned int dictSdsCaseHash(const void *key) { +uint64_t dictSdsCaseHash(const void *key) { return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key)); } @@ -513,7 +513,7 @@ int dictEncObjKeyCompare(void *privdata, const void *key1, return cmp; } -unsigned int dictEncObjHash(const void *key) { +uint64_t dictEncObjHash(const void *key) { robj *o = (robj*) key; if (sdsEncodedObject(o)) { @@ -526,7 +526,7 @@ unsigned int dictEncObjHash(const void *key) { len = ll2string(buf,32,(long)o->ptr); return dictGenHashFunction((unsigned char*)buf, len); } else { - unsigned int hash; + uint64_t hash; o = getDecodedObject(o); hash = dictGenHashFunction(o->ptr, sdslen((sds)o->ptr)); @@ -3639,7 +3639,9 @@ int main(int argc, char **argv) { zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand(time(NULL)^getpid()); gettimeofday(&tv,NULL); - dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid()); + char hashseed[16]; + getRandomHexChars(hashseed,sizeof(hashseed)); + dictSetHashFunctionSeed((uint8_t*)hashseed); server.sentinel_mode = checkForSentinelMode(argc,argv); initServerConfig(); moduleInitModulesSystem(); diff --git a/src/server.h b/src/server.h index 30d8be84..75ff384c 100644 --- a/src/server.h +++ b/src/server.h @@ -1765,7 +1765,7 @@ unsigned long LFUGetTimeInMinutes(void); uint8_t LFULogIncr(uint8_t value); /* Keys hashing / comparison functions for dict.c hash tables. */ -unsigned int dictSdsHash(const void *key); +uint64_t dictSdsHash(const void *key); int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); void dictSdsDestructor(void *privdata, void *val); diff --git a/src/siphash.c b/src/siphash.c new file mode 100644 index 00000000..04e571fd --- /dev/null +++ b/src/siphash.c @@ -0,0 +1,328 @@ +/* + SipHash reference C implementation + + Copyright (c) 2012-2016 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + Copyright (c) 2017 Salvatore Sanfilippo + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with this software. If not, see + . + + ---------------------------------------------------------------------------- + + This version was modified by Salvatore Sanfilippo + in the following ways: + + 1. Hard-code 2-4 rounds in the hope the compiler can optimize it more + in this raw from. Anyway we always want the standard 2-4 variant. + 2. Modify the prototype and implementation so that the function directly + returns an uint64_t value, the hash itself, instead of receiving an + output buffer. This also means that the output size is set to 8 bytes + and the 16 bytes output code handling was removed. + 3. Provide a case insensitive variant to be used when hashing strings that + must be considered identical by the hash table regardless of the case. + If we don't have directly a case insensitive hash function, we need to + perform a text transformation in some temporary buffer, which is costly. + 4. Remove debugging code. + 5. Modified the original test.c file to be a stand-alone function testing + the function in the new form (returing an uint64_t) using just the + relevant test vector. + */ +#include +#include +#include +#include +#include + +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define U32TO8_LE(p, v) \ + (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); + +#define U64TO8_LE(p, v) \ + U32TO8_LE((p), (uint32_t)((v))); \ + U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); + +#define U8TO64_LE(p) \ + (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ + ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ + ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ + ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) + +#define U8TO64_LE_NOCASE(p) \ + (((uint64_t)(tolower((p)[0]))) | \ + ((uint64_t)(tolower((p)[1])) << 8) | \ + ((uint64_t)(tolower((p)[2])) << 16) | \ + ((uint64_t)(tolower((p)[3])) << 24) | \ + ((uint64_t)(tolower((p)[4])) << 32) | \ + ((uint64_t)(tolower((p)[5])) << 40) | \ + ((uint64_t)(tolower((p)[6])) << 48) | \ + ((uint64_t)(tolower((p)[7])) << 56)) + +#define SIPROUND \ + do { \ + v0 += v1; \ + v1 = ROTL(v1, 13); \ + v1 ^= v0; \ + v0 = ROTL(v0, 32); \ + v2 += v3; \ + v3 = ROTL(v3, 16); \ + v3 ^= v2; \ + v0 += v3; \ + v3 = ROTL(v3, 21); \ + v3 ^= v0; \ + v2 += v1; \ + v1 = ROTL(v1, 17); \ + v1 ^= v2; \ + v2 = ROTL(v2, 32); \ + } while (0) + +uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) { + uint64_t hash; + uint8_t *out = (uint8_t*) &hash; + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + uint64_t k0 = U8TO64_LE(k); + uint64_t k1 = U8TO64_LE(k + 8); + uint64_t m; + const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + uint64_t b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + for (; in != end; in += 8) { + m = U8TO64_LE(in); + v3 ^= m; + + SIPROUND; + SIPROUND; + + v0 ^= m; + } + + switch (left) { + case 7: b |= ((uint64_t)in[6]) << 48; + case 6: b |= ((uint64_t)in[5]) << 40; + case 5: b |= ((uint64_t)in[4]) << 32; + case 4: b |= ((uint64_t)in[3]) << 24; + case 3: b |= ((uint64_t)in[2]) << 16; + case 2: b |= ((uint64_t)in[1]) << 8; + case 1: b |= ((uint64_t)in[0]); break; + case 0: break; + } + + v3 ^= b; + + SIPROUND; + SIPROUND; + + v0 ^= b; + v2 ^= 0xff; + + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; + U64TO8_LE(out, b); + + return hash; +} + +uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k) +{ + uint64_t hash; + uint8_t *out = (uint8_t*) &hash; + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + uint64_t k0 = U8TO64_LE(k); + uint64_t k1 = U8TO64_LE(k + 8); + uint64_t m; + const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + uint64_t b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + for (; in != end; in += 8) { + m = U8TO64_LE_NOCASE(in); + v3 ^= m; + + SIPROUND; + SIPROUND; + + v0 ^= m; + } + + switch (left) { + case 7: b |= ((uint64_t)tolower(in[6])) << 48; + case 6: b |= ((uint64_t)tolower(in[5])) << 40; + case 5: b |= ((uint64_t)tolower(in[4])) << 32; + case 4: b |= ((uint64_t)tolower(in[3])) << 24; + case 3: b |= ((uint64_t)tolower(in[2])) << 16; + case 2: b |= ((uint64_t)tolower(in[1])) << 8; + case 1: b |= ((uint64_t)tolower(in[0])); break; + case 0: break; + } + + v3 ^= b; + + SIPROUND; + SIPROUND; + + v0 ^= b; + v2 ^= 0xff; + + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; + U64TO8_LE(out, b); + + return hash; +} + + +/* --------------------------------- TEST ------------------------------------ */ + +#ifdef SIPHASH_TEST + +const uint8_t vectors_sip64[64][8] = { + { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, }, + { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, }, + { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, }, + { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, }, + { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, }, + { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, }, + { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, }, + { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, }, + { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, }, + { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, }, + { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, }, + { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, }, + { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, }, + { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, }, + { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, }, + { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, }, + { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, }, + { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, }, + { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, }, + { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, }, + { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, }, + { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, }, + { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, }, + { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, }, + { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, }, + { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, }, + { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, }, + { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, }, + { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, }, + { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, }, + { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, }, + { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, }, + { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, }, + { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, }, + { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, }, + { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, }, + { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, }, + { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, }, + { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, }, + { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, }, + { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, }, + { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, }, + { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, }, + { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, }, + { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, }, + { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, }, + { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, }, + { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, }, + { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, }, + { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, }, + { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, }, + { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, }, + { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, }, + { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, }, + { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, }, + { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, }, + { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, }, + { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, }, + { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, }, + { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, }, + { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, }, + { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, }, + { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, }, + { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }, +}; + + +/* Test siphash using a test vector. Returns 0 if the function passed + * all the tests, otherwise 1 is returned. */ +int siphash_test(void) { + uint8_t in[64], k[16]; + int i; + int fails = 0; + + for (i = 0; i < 16; ++i) + k[i] = i; + + for (i = 0; i < 64; ++i) { + in[i] = i; + uint64_t hash = siphash(in, i, k); + const uint8_t *v = NULL; + v = (uint8_t *)vectors_sip64; + if (memcmp(&hash, v + (i * 8), 8)) { + /* printf("fail for %d bytes\n", i); */ + fails++; + } + } + + /* Run a few basic tests with the case insensitive version. */ + uint64_t h1, h2; + h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678"); + h2 = siphash_nocase((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678"); + if (h1 != h2) fails++; + + h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678"); + h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678"); + if (h1 != h2) fails++; + + h1 = siphash((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678"); + h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678"); + if (h1 == h2) fails++; + + if (!fails) return 0; + return 1; +} + +int main(void) { + if (siphash_test() == 0) { + printf("SipHash test: OK\n"); + return 0; + } else { + printf("SipHash test: FAILED\n"); + return 1; + } +} + +#endif diff --git a/src/t_zset.c b/src/t_zset.c index d36fa30a..f7f4c6eb 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -2110,7 +2110,7 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat } } -unsigned int dictSdsHash(const void *key); +uint64_t dictSdsHash(const void *key); int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); dictType setAccumulatorDictType = {