Commit 4e04e3d8 authored by Ondřej Surý's avatar Ondřej Surý
Browse files

Convert isc_hash functions to use isc_siphash24

(cherry picked from commit 2e7d8244)
parent 2188a581
......@@ -80,10 +80,9 @@ dyndb_init(isc_mem_t *mctx, const char *name, const char *parameters,
isc_lib_register();
isc_log_setcontext(dctx->lctx);
dns_log_setcontext(dctx->lctx);
isc_hash_set_initializer(dctx->hashinit);
}
isc_hash_set_initializer(dctx->hashinit);
s = isc_mem_strdup(mctx, parameters);
if (s == NULL) {
result = ISC_R_NOMEMORY;
......
......@@ -466,7 +466,7 @@ dns_name_hash(const dns_name_t *name, bool case_sensitive) {
length = 16;
return (isc_hash_function_reverse(name->ndata, length,
case_sensitive, NULL));
case_sensitive));
}
unsigned int
......@@ -480,7 +480,7 @@ dns_name_fullhash(const dns_name_t *name, bool case_sensitive) {
return (0);
return (isc_hash_function_reverse(name->ndata, name->length,
case_sensitive, NULL));
case_sensitive));
}
dns_namereln_t
......
......@@ -9655,7 +9655,7 @@ rehash_gluetable(rbtdb_version_t *version) {
{
hash = isc_hash_function(&gluenode->node,
sizeof(gluenode->node),
true, NULL) %
true) %
version->glue_table_size;
nextgluenode = gluenode->next;
gluenode->next = version->glue_table[hash];
......@@ -9823,7 +9823,7 @@ rdataset_addglue(dns_rdataset_t *rdataset, dns_dbversion_t *version,
* the node pointer is a fixed value that won't change for a DB
* version and can be compared directly.
*/
idx = isc_hash_function(&node, sizeof(node), true, NULL) %
idx = isc_hash_function(&node, sizeof(node), true) %
rbtversion->glue_table_size;
restart:
......@@ -9999,8 +9999,7 @@ no_glue:
RWLOCK(&rbtversion->glue_rwlock, isc_rwlocktype_write);
if (ISC_UNLIKELY(rehash_gluetable(rbtversion))) {
idx = isc_hash_function(&node, sizeof(node),
true, NULL) %
idx = isc_hash_function(&node, sizeof(node), true) %
rbtversion->glue_table_size;
}
......
......@@ -18,6 +18,9 @@
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#if defined(WIN32) || defined(WIN64)
#include <malloc.h>
#endif
#include "isc/hash.h" // IWYU pragma: keep
#include "isc/likely.h"
......@@ -26,12 +29,31 @@
#include "isc/result.h"
#include "isc/types.h"
#include "isc/util.h"
#include "isc/siphash.h"
#include "isc/string.h"
static uint32_t fnv_offset_basis;
static isc_once_t fnv_once = ISC_ONCE_INIT;
static bool fnv_initialized = false;
#include "entropy_private.h"
static unsigned char maptolower[] = {
static uint8_t isc_hash_key[16];
static bool hash_initialized = false;
static isc_once_t isc_hash_once = ISC_ONCE_INIT;
static void
isc_hash_initialize(void) {
uint64_t key[2] = { 0, 1 };
#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/*
* Set a constant key to help in problem reproduction should
* fuzzing find a crash or a hang.
*/
#else
isc_entropy_get(key, sizeof(key));
#endif
memmove(isc_hash_key, key, sizeof(isc_hash_key));
hash_initialized = true;
}
static uint8_t maptolower[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
......@@ -66,27 +88,15 @@ static unsigned char maptolower[] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
static void
fnv_initialize(void) {
/*
* This function should not leave fnv_offset_basis set to
* 0. Also, after this function has been called, if it is called
* again, it should not change fnv_offset_basis.
*/
while (fnv_offset_basis == 0) {
fnv_offset_basis = isc_random32();
}
fnv_initialized = true;
}
const void *
isc_hash_get_initializer(void) {
if (ISC_UNLIKELY(!fnv_initialized))
RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) ==
ISC_R_SUCCESS);
if (ISC_UNLIKELY(!hash_initialized)) {
RUNTIME_CHECK(isc_once_do(&isc_hash_once,
isc_hash_initialize)
== ISC_R_SUCCESS);
}
return (&fnv_offset_basis);
return (isc_hash_key);
}
void
......@@ -94,112 +104,70 @@ isc_hash_set_initializer(const void *initializer) {
REQUIRE(initializer != NULL);
/*
* Ensure that fnv_initialize() is not called after
* Ensure that isc_hash_initialize() is not called after
* isc_hash_set_initializer() is called.
*/
if (ISC_UNLIKELY(!fnv_initialized))
RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) ==
ISC_R_SUCCESS);
if (ISC_UNLIKELY(!hash_initialized)) {
RUNTIME_CHECK(isc_once_do(&isc_hash_once,
isc_hash_initialize)
== ISC_R_SUCCESS);
}
fnv_offset_basis = *((const unsigned int *)initializer);
memmove(isc_hash_key, initializer, sizeof(isc_hash_key));
}
#define FNV_32_PRIME ((uint32_t)0x01000193)
uint32_t
isc_hash_function(const void *data, size_t length, bool case_sensitive,
const uint32_t *previous_hashp)
uint64_t
isc_hash_function(const void *data, const size_t length,
const bool case_sensitive)
{
uint32_t hval;
const unsigned char *bp;
const unsigned char *be;
uint64_t hval;
REQUIRE(length == 0 || data != NULL);
if (ISC_UNLIKELY(!fnv_initialized)) {
RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) ==
ISC_R_SUCCESS);
}
hval = ISC_UNLIKELY(previous_hashp != NULL) ? *previous_hashp
: fnv_offset_basis;
if (length == 0) {
return (hval);
}
bp = (const unsigned char *)data;
be = bp + length;
/*
* Fowler-Noll-Vo FNV-1a hash function.
*
* NOTE: A random FNV offset basis is used by default to avoid
* collision attacks as the hash function is reversible. This
* makes the mapping non-deterministic, but the distribution in
* the domain is still uniform.
*/
RUNTIME_CHECK(isc_once_do(&isc_hash_once,
isc_hash_initialize) == ISC_R_SUCCESS);
if (case_sensitive) {
while (bp < be) {
hval ^= *bp++;
hval *= FNV_32_PRIME;
}
isc_siphash24(isc_hash_key, data, length, (uint8_t *)&hval);
} else {
while (bp < be) {
hval ^= maptolower[*bp++];
hval *= FNV_32_PRIME;
uint8_t input[1024];
REQUIRE(length <= 1024);
for (unsigned int i = 0; i < length; i++) {
input[i] = maptolower[((const uint8_t *)data)[i]];
}
isc_siphash24(isc_hash_key, input, length, (uint8_t *)&hval);
}
return (hval);
}
uint32_t
isc_hash_function_reverse(const void *data, size_t length, bool case_sensitive,
const uint32_t *previous_hashp)
uint64_t
isc_hash_function_reverse(const void *data, const size_t length,
const bool case_sensitive)
{
uint32_t hval;
const unsigned char *bp;
const unsigned char *be;
uint64_t hval;
#if defined(WIN32) || defined(WIN64)
uint8_t *input = _alloca(length);
INSIST(buf != NULL);
#else
uint8_t input[length];
#endif
REQUIRE(length == 0 || data != NULL);
if (ISC_UNLIKELY(!fnv_initialized)) {
RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) ==
ISC_R_SUCCESS);
}
hval = ISC_UNLIKELY(previous_hashp != NULL) ? *previous_hashp
: fnv_offset_basis;
if (length == 0) {
return (hval);
}
bp = (const unsigned char *)data;
be = bp + length;
/*
* Fowler-Noll-Vo FNV-1a hash function.
*
* NOTE: A random FNV offset basis is used by default to avoid
* collision attacks as the hash function is reversible. This
* makes the mapping non-deterministic, but the distribution in
* the domain is still uniform.
*/
RUNTIME_CHECK(isc_once_do(&isc_hash_once,
isc_hash_initialize) == ISC_R_SUCCESS);
if (case_sensitive) {
while (--be >= bp) {
hval ^= *be;
hval *= FNV_32_PRIME;
for (unsigned int i = 0, j = length - 1; i < length; i++, j--) {
input[i] = ((const uint8_t *)data)[j];
}
} else {
while (--be >= bp) {
hval ^= maptolower[*be];
hval *= FNV_32_PRIME;
for (unsigned int i = 0, j = length - 1; i < length; i++, j--) {
input[i] = maptolower[((const uint8_t *)data)[j]];
}
}
isc_siphash24(isc_hash_key, input, length, (uint8_t *)&hval);
return (hval);
}
......@@ -130,7 +130,7 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key,
REQUIRE(ISC_HT_VALID(ht));
REQUIRE(key != NULL && keysize > 0);
hash = isc_hash_function(key, keysize, true, NULL);
hash = isc_hash_function(key, keysize, true);
node = ht->table[hash & ht->mask];
while (node != NULL) {
if (keysize == node->keysize &&
......@@ -165,7 +165,7 @@ isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
REQUIRE(key != NULL && keysize > 0);
REQUIRE(valuep == NULL || *valuep == NULL);
hash = isc_hash_function(key, keysize, true, NULL);
hash = isc_hash_function(key, keysize, true);
node = ht->table[hash & ht->mask];
while (node != NULL) {
if (keysize == node->keysize &&
......@@ -190,7 +190,7 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) {
REQUIRE(key != NULL && keysize > 0);
prev = NULL;
hash = isc_hash_function(key, keysize, true, NULL);
hash = isc_hash_function(key, keysize, true);
node = ht->table[hash & ht->mask];
while (node != NULL) {
if (keysize == node->keysize &&
......@@ -305,8 +305,7 @@ isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
it->cur = ht->table[it->i];
}
hash = isc_hash_function(to_delete->key, to_delete->keysize, true,
NULL);
hash = isc_hash_function(to_delete->key, to_delete->keysize, true);
node = ht->table[hash & ht->mask];
while (node != to_delete) {
prev = node;
......
......@@ -29,14 +29,10 @@ isc_hash_get_initializer(void);
void
isc_hash_set_initializer(const void *initializer);
uint32_t
isc_hash_function(const void *data, size_t length,
bool case_sensitive,
const uint32_t *previous_hashp);
uint32_t
isc_hash_function_reverse(const void *data, size_t length,
bool case_sensitive,
const uint32_t *previous_hashp);
uint64_t
isc_hash_function(const void *data, const size_t length, const bool case_sensitive);
uint64_t
isc_hash_function_reverse(const void *data, const size_t length, const bool case_sensitive);
/*!<
* \brief Calculate a hash over data.
*
......
......@@ -263,7 +263,7 @@ add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
if (mctx->debuglist == NULL)
return;
hash = isc_hash_function(&ptr, sizeof(ptr), true, NULL);
hash = isc_hash_function(&ptr, sizeof(ptr), true);
idx = hash % DEBUG_TABLE_COUNT;
dl = malloc(sizeof(debuglink_t));
......@@ -298,7 +298,7 @@ delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
if (mctx->debuglist == NULL)
return;
hash = isc_hash_function(&ptr, sizeof(ptr), true, NULL);
hash = isc_hash_function(&ptr, sizeof(ptr), true);
idx = hash % DEBUG_TABLE_COUNT;
dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
......
......@@ -16,6 +16,9 @@
#include <stdbool.h>
#include <stdio.h>
#if defined(WIN32) || defined(WIN64)
#include <malloc.h>
#endif
#include <isc/buffer.h>
#include <isc/hash.h>
......@@ -224,9 +227,14 @@ isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, bool address_only) {
p = 0;
}
h = isc_hash_function(s, length, true, NULL);
if (!address_only)
h = isc_hash_function(&p, sizeof(p), true, &h);
uint8_t buf[sizeof(struct sockaddr_storage) + sizeof(p)];
memmove(buf, s, length);
if (!address_only) {
memmove(buf + length, &p, sizeof(p));
h = isc_hash_function(buf, length + sizeof(p), true);
} else {
h = isc_hash_function(buf, length, true);
}
return (h);
}
......
......@@ -39,13 +39,6 @@
#define TEST_INPUT(x) (x), sizeof(x)-1
typedef struct hash_testcase {
const char *input;
size_t input_len;
const char *result;
int repeats;
} hash_testcase_t;
/*Hash function test */
static void
isc_hash_function_test(void **state) {
......@@ -54,39 +47,27 @@ isc_hash_function_test(void **state) {
UNUSED(state);
/* Incremental hashing */
h1 = isc_hash_function(NULL, 0, true, NULL);
h1 = isc_hash_function("This ", 5, true, &h1);
h1 = isc_hash_function("is ", 3, true, &h1);
h1 = isc_hash_function("a long test", 12, true, &h1);
h2 = isc_hash_function("This is a long test", 20,
true, NULL);
assert_int_equal(h1, h2);
/* Immutability of hash function */
h1 = isc_hash_function(NULL, 0, true, NULL);
h2 = isc_hash_function(NULL, 0, true, NULL);
h1 = isc_hash_function(NULL, 0, true);
h2 = isc_hash_function(NULL, 0, true);
assert_int_equal(h1, h2);
/* Hash function characteristics */
h1 = isc_hash_function("Hello world", 12, true, NULL);
h2 = isc_hash_function("Hello world", 12, true, NULL);
h1 = isc_hash_function("Hello world", 12, true);
h2 = isc_hash_function("Hello world", 12, true);
assert_int_equal(h1, h2);
/* Case */
h1 = isc_hash_function("Hello world", 12, false, NULL);
h2 = isc_hash_function("heLLo WorLd", 12, false, NULL);
h1 = isc_hash_function("Hello world", 12, false);
h2 = isc_hash_function("heLLo WorLd", 12, false);
assert_int_equal(h1, h2);
/* Unequal */
h1 = isc_hash_function("Hello world", 12, true, NULL);
h2 = isc_hash_function("heLLo WorLd", 12, true, NULL);
h1 = isc_hash_function("Hello world", 12, true);
h2 = isc_hash_function("heLLo WorLd", 12, true);
assert_int_not_equal(h1, h2);
}
......@@ -99,39 +80,27 @@ isc_hash_function_reverse_test(void **state) {
UNUSED(state);
/* Incremental hashing */
h1 = isc_hash_function_reverse(NULL, 0, true, NULL);
h1 = isc_hash_function_reverse("\000", 1, true, &h1);
h1 = isc_hash_function_reverse("\003org", 4, true, &h1);
h1 = isc_hash_function_reverse("\007example", 8, true, &h1);
h2 = isc_hash_function_reverse("\007example\003org\000", 13,
true, NULL);
assert_int_equal(h1, h2);
/* Immutability of hash function */
h1 = isc_hash_function_reverse(NULL, 0, true, NULL);
h2 = isc_hash_function_reverse(NULL, 0, true, NULL);
h1 = isc_hash_function_reverse(NULL, 0, true);
h2 = isc_hash_function_reverse(NULL, 0, true);
assert_int_equal(h1, h2);
/* Hash function characteristics */
h1 = isc_hash_function_reverse("Hello world", 12, true, NULL);
h2 = isc_hash_function_reverse("Hello world", 12, true, NULL);
h1 = isc_hash_function_reverse("Hello world", 12, true);
h2 = isc_hash_function_reverse("Hello world", 12, true);
assert_int_equal(h1, h2);
/* Case */
h1 = isc_hash_function_reverse("Hello world", 12, false, NULL);
h2 = isc_hash_function_reverse("heLLo WorLd", 12, false, NULL);
h1 = isc_hash_function_reverse("Hello world", 12, false);
h2 = isc_hash_function_reverse("heLLo WorLd", 12, false);
assert_int_equal(h1, h2);
/* Unequal */
h1 = isc_hash_function_reverse("Hello world", 12, true, NULL);
h2 = isc_hash_function_reverse("heLLo WorLd", 12, true, NULL);
h1 = isc_hash_function_reverse("Hello world", 12, true);
h2 = isc_hash_function_reverse("heLLo WorLd", 12, true);
assert_true(h1 != h2);
}
......@@ -144,15 +113,15 @@ isc_hash_initializer_test(void **state) {
UNUSED(state);
h1 = isc_hash_function("Hello world", 12, true, NULL);
h2 = isc_hash_function("Hello world", 12, true, NULL);
h1 = isc_hash_function("Hello world", 12, true);
h2 = isc_hash_function("Hello world", 12, true);
assert_int_equal(h1, h2);
isc_hash_set_initializer(isc_hash_get_initializer());
/* Hash value must not change */
h2 = isc_hash_function("Hello world", 12, true, NULL);
h2 = isc_hash_function("Hello world", 12, true);
assert_int_equal(h1, h2);
}
......
......@@ -235,7 +235,6 @@ isc_fsaccess_changeowner
isc_fsaccess_remove
isc_fsaccess_set
isc_hash_function
isc_hash_function_reverse
isc_hash_get_initializer
isc_hash_set_initializer
isc_heap_create
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment