hashtable: Support lockless reads

Also build it in the FIPS provider too and properly
report error on insert when hashtable cannot be grown.

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Paul Dale <ppzgs1@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/24504)
This commit is contained in:
Tomas Mraz 2024-05-27 16:49:15 +02:00
parent 6cdca7b9fe
commit 71fe7f0983
7 changed files with 33 additions and 16 deletions

View File

@ -1,4 +1,6 @@
LIBS=../../libcrypto
SOURCE[../../libcrypto]=\
hashtable.c
$COMMON=hashtable.c
SOURCE[../../libcrypto]=$COMMON
SOURCE[../../providers/libfips.a]=$COMMON

View File

@ -179,7 +179,7 @@ static void internal_free_nop(HT_VALUE *v)
return;
}
HT *ossl_ht_new(HT_CONFIG *conf)
HT *ossl_ht_new(const HT_CONFIG *conf)
{
HT *new = OPENSSL_zalloc(sizeof(*new));
@ -427,7 +427,7 @@ static void free_old_neigh_table(void *arg)
*/
static int grow_hashtable(HT *h, size_t oldsize)
{
struct ht_mutable_data_st *newmd = OPENSSL_zalloc(sizeof(*newmd));
struct ht_mutable_data_st *newmd;
struct ht_mutable_data_st *oldmd = ossl_rcu_deref(&h->md);
int rc = 0;
uint64_t oldi, oldj, newi, newj;
@ -436,7 +436,10 @@ static int grow_hashtable(HT *h, size_t oldsize)
int rehashed;
size_t newsize = oldsize * 2;
if (newmd == NULL)
if (h->config.lockless_reads)
goto out;
if ((newmd = OPENSSL_zalloc(sizeof(*newmd))) == NULL)
goto out;
/* bucket list is always a power of 2 */
@ -700,6 +703,9 @@ int ossl_ht_delete(HT *h, HT_KEY *key)
HT_VALUE *nv = NULL;
int rc = 0;
if (h->config.lockless_reads)
return 0;
hash = h->config.ht_hash_fn(key->keybuf, key->keysize);
neigh_idx = hash & h->md->neighborhood_mask;
@ -724,4 +730,3 @@ int ossl_ht_delete(HT *h, HT_KEY *key)
}
return rc;
}

View File

@ -20,7 +20,7 @@ IMPLEMENT_HT_VALUE_TYPE_FNS
=head1 SYNOPSIS
HT *ossl_ht_new(HT_CONFIG *conf);
HT *ossl_ht_new(const HT_CONFIG *conf);
void ossl_ht_free(HT *htable);
void ossl_ht_read_lock(HT *htable);
void ossl_ht_read_unlock(HT *htable);
@ -66,14 +66,14 @@ contains configurations options for hashtable. Current config options consist
of:
I<ht_free_fn> The function to call to free a value, may be NULL.
I<ht_hash_fn> The function to generate a hash value for a key, may be NULL.
I<init_neighborhood_len> The initial number of neighborhoods in the hash table.
I<init_neighborhoods> The initial number of neighborhoods in the hash table.
Note that init_bucket_len may be set to zero, which will use the default initial
bucket size, which will be automatically expanded with the hash table load
average reaches 0.75.
Note that lockless_read operation implies behavioral restrictions. Specifically
Only element additions are allowed, deletion operations will fail
only element additions are allowed, deletion operations will fail
Hash table growth is inhibited. init_bucket_len should be set to an
appropriate value to prevent performance degradation
The table owner is responsible for ensuring there are no readers during a
@ -281,6 +281,9 @@ provided filter
ossl_ht_get() returns an B<HT_VALUE> pointer, or NULL if the element was not
found.
ossl_ht_insert() returns 1 if an element was inserted, 0 if the element is
already present, -1 on fatal errors (memory allocation or growth not allowed).
=head1 EXAMPLES
#include <stdio.h>

View File

@ -99,7 +99,7 @@ static void fuzz_free_cb(HT_VALUE *v)
int FuzzerInitialize(int *argc, char ***argv)
{
HT_CONFIG fuzz_conf = {NULL, fuzz_free_cb, NULL, 1, 0};
HT_CONFIG fuzz_conf = {NULL, fuzz_free_cb, NULL, 0, 1};
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
ERR_clear_error();

View File

@ -51,8 +51,9 @@ typedef struct ht_config_st {
OSSL_LIB_CTX *ctx;
void (*ht_free_fn)(HT_VALUE *obj);
uint64_t (*ht_hash_fn)(uint8_t *key, size_t keylen);
size_t init_neighborhoods;
uint32_t collision_check;
uint32_t init_neighborhoods;
uint32_t lockless_reads;
} HT_CONFIG;
/*
@ -249,7 +250,7 @@ static void ossl_unused ossl_ht_strcase(char *tgt, const char *src, int len)
/*
* Create a new hashtable
*/
HT *ossl_ht_new(HT_CONFIG *conf);
HT *ossl_ht_new(const HT_CONFIG *conf);
/*
* Frees a hash table, potentially freeing all elements

View File

@ -1134,6 +1134,12 @@ int CRYPTO_secure_allocated(const void *ptr)
return c_CRYPTO_secure_allocated(ptr);
}
void *CRYPTO_aligned_alloc(size_t num, size_t align, void **freeptr,
const char *file, int line)
{
return NULL;
}
int BIO_snprintf(char *buf, size_t n, const char *format, ...)
{
va_list args;

View File

@ -230,8 +230,8 @@ static int test_int_hashtable(void)
NULL,
NULL,
NULL,
1,
0,
1,
};
INTKEY key;
int rc = 0;
@ -408,8 +408,8 @@ static int test_hashtable_stress(void)
NULL, /* use default context */
hashtable_intfree, /* our free function */
hashtable_hash, /* our hash function */
1, /* Check collisions */
625000, /* preset hash size */
1, /* Check collisions */
};
HT *h;
INTKEY key;
@ -627,8 +627,8 @@ static int test_hashtable_multithread(void)
NULL, /* use default context */
hashtable_mt_free, /* our free function */
NULL, /* default hash function */
1, /* Check collisions */
0, /* default hash size */
1, /* Check collisions */
};
int ret = 0;
thread_t workers[4];