summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2017-02-20 16:09:54 +0100
committerantirez <antirez@gmail.com>2017-02-20 17:29:17 +0100
commitadeed29a99dcd0efdbfe4dbd5da74e7b01966c67 (patch)
tree73feb624357ce5f8bbf82028e75968b67358e3f1
parent9b05aafb50348838f45bfddcd689e7d8d1d3c950 (diff)
downloadredis-adeed29a99dcd0efdbfe4dbd5da74e7b01966c67.tar.gz
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.
-rw-r--r--src/Makefile2
-rw-r--r--src/config.c2
-rw-r--r--src/debug.c2
-rw-r--r--src/dict.c75
-rw-r--r--src/dict.h10
-rw-r--r--src/latency.c2
-rw-r--r--src/module.c2
-rw-r--r--src/sentinel.c2
-rw-r--r--src/server.c14
-rw-r--r--src/server.h2
-rw-r--r--src/siphash.c328
-rw-r--r--src/t_zset.c2
12 files changed, 361 insertions, 82 deletions
diff --git a/src/Makefile b/src/Makefile
index 3f445f40f..5f7536e83 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 83651877c..900274f65 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 a6bc62dc8..a4caa49f2 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 59aef7724..8ce735961 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -37,11 +37,11 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <sys/time.h>
-#include <ctype.h>
#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;
-
- /* Initialize the hash to a 'random' value */
- uint32_t h = seed ^ len;
-
- /* 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;
+/* The default hashing function uses SipHash implementation
+ * in siphash.c. */
- h *= m;
- h ^= k;
+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);
- 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 60a423a2c..bf316a00f 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 53e0ec7be..9e9f1f13a 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 1fbc5094f..3b90eae4a 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 1f47dd337..6c6a3a0cd 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 8bf6510de..0494a4e75 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 30d8be849..75ff384cd 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 000000000..04e571fd7
--- /dev/null
+++ b/src/siphash.c
@@ -0,0 +1,328 @@
+/*
+ SipHash reference C implementation
+
+ Copyright (c) 2012-2016 Jean-Philippe Aumasson
+ <jeanphilippe.aumasson@gmail.com>
+ Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
+ Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>
+
+ 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
+ <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+ ----------------------------------------------------------------------------
+
+ This version was modified by Salvatore Sanfilippo <antirez@gmail.com>
+ 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 <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#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 d36fa30ae..f7f4c6eb2 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 = {