Coverage Report

Created: 2025-12-31 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl35/crypto/hashtable/hashtable.c
Line
Count
Source
1
/*
2
 * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 *
9
 *
10
 *
11
 * Notes On hash table design and layout
12
 * This hashtable uses a hopscotch algorithm to do indexing.  The data structure
13
 * looks as follows:
14
 *
15
 *   hash          +--------------+
16
 *   value+------->+ HT_VALUE     |
17
 *      +          +--------------+
18
 *  +-------+
19
 *  |       |
20
 *  +---------------------------------------------------------+
21
 *  |       |       |       |       |                         |
22
 *  | entry | entry | entry | entry |                         |
23
 *  |       |       |       |       |                         |
24
 *  +---------------------------------------------------------+
25
 *  |                               |                         |
26
 *  |                               |                         |
27
 *  +---------------------------------------------------------+
28
 *  |              +                             +            +
29
 *  |        neighborhood[0]               neighborhood[1]    |
30
 *  |                                                         |
31
 *  |                                                         |
32
 *  +---------------------------------------------------------+
33
 *                              |
34
 *                              +
35
 *                         neighborhoods
36
 *
37
 * On lookup/insert/delete, the items key is hashed to a 64 bit value
38
 * and the result is masked to provide an index into the neighborhoods
39
 * table.  Once a neighborhood is determined, an in-order search is done
40
 * of the elements in the neighborhood indexes entries for a matching hash
41
 * value, if found, the corresponding HT_VALUE is used for the respective
42
 * operation.  The number of entries in a neighborhood is determined at build
43
 * time based on the cacheline size of the target CPU.  The intent is for a
44
 * neighborhood to have all entries in the neighborhood fit into a single cache
45
 * line to speed up lookups.  If all entries in a neighborhood are in use at the
46
 * time of an insert, the table is expanded and rehashed.
47
 *
48
 * Lockless reads hash table is based on the same design but does not
49
 * allow growing and deletion. Thus subsequent neighborhoods are always
50
 * searched for a match until an empty entry is found.
51
 */
52
53
#include <string.h>
54
#include <internal/rcu.h>
55
#include <internal/hashtable.h>
56
#include <internal/hashfunc.h>
57
#include <openssl/rand.h>
58
59
/*
60
 * gcc defines __SANITIZE_THREAD__
61
 * but clang uses the feature attributes api
62
 * map the latter to the former
63
 */
64
#if defined(__clang__) && defined(__has_feature)
65
#if __has_feature(thread_sanitizer)
66
#define __SANITIZE_THREADS__
67
#endif
68
#endif
69
70
#ifdef __SANITIZE_THREADS__
71
#include <sanitizer/tsan_interface.h>
72
#endif
73
74
#include "internal/numbers.h"
75
/*
76
 * When we do a lookup/insert/delete, there is a high likelihood
77
 * that we will iterate over at least part of the neighborhood list
78
 * As such, because we design a neighborhood entry to fit into a single
79
 * cache line it is advantageous, when supported to fetch the entire
80
 * structure for faster lookups
81
 */
82
#if defined(__GNUC__) || defined(__CLANG__)
83
25.0M
#define PREFETCH_NEIGHBORHOOD(x) __builtin_prefetch(x.entries)
84
61.2M
#define PREFETCH(x) __builtin_prefetch(x)
85
#else
86
#define PREFETCH_NEIGHBORHOOD(x)
87
#define PREFETCH(x)
88
#endif
89
90
/*
91
 * Define our neighborhood list length
92
 * Note: It should always be a power of 2
93
 */
94
477
#define DEFAULT_NEIGH_LEN_LOG 4
95
477
#define DEFAULT_NEIGH_LEN (1 << DEFAULT_NEIGH_LEN_LOG)
96
97
/*
98
 * For now assume cache line size is 64 bytes
99
 */
100
33.4M
#define CACHE_LINE_BYTES 64
101
#define CACHE_LINE_ALIGNMENT CACHE_LINE_BYTES
102
103
33.4M
#define NEIGHBORHOOD_LEN (CACHE_LINE_BYTES / sizeof(struct ht_neighborhood_entry_st))
104
/*
105
 * Defines our chains of values
106
 */
107
struct ht_internal_value_st {
108
    HT_VALUE value;
109
    HT *ht;
110
};
111
112
struct ht_neighborhood_entry_st {
113
    uint64_t hash;
114
    struct ht_internal_value_st *value;
115
};
116
117
struct ht_neighborhood_st {
118
    struct ht_neighborhood_entry_st entries[NEIGHBORHOOD_LEN];
119
};
120
121
/*
122
 * Updates to data in this struct
123
 * require an rcu sync after modification
124
 * prior to free
125
 */
126
struct ht_mutable_data_st {
127
    struct ht_neighborhood_st *neighborhoods;
128
    void *neighborhood_ptr_to_free;
129
    uint64_t neighborhood_mask;
130
};
131
132
/*
133
 * Private data may be updated on the write
134
 * side only, and so do not require rcu sync
135
 */
136
struct ht_write_private_data_st {
137
    size_t neighborhood_len;
138
    size_t value_count;
139
    int need_sync;
140
};
141
142
struct ht_internal_st {
143
    HT_CONFIG config;
144
    CRYPTO_RCU_LOCK *lock;
145
    CRYPTO_RWLOCK *atomic_lock;
146
    struct ht_mutable_data_st *md;
147
    struct ht_write_private_data_st wpd;
148
};
149
150
static void free_value(struct ht_internal_value_st *v);
151
152
static struct ht_neighborhood_st *alloc_new_neighborhood_list(size_t len,
153
    void **freeptr)
154
62.7k
{
155
62.7k
    struct ht_neighborhood_st *ret;
156
157
62.7k
    ret = OPENSSL_aligned_alloc(sizeof(struct ht_neighborhood_st) * len,
158
62.7k
        CACHE_LINE_BYTES, freeptr);
159
160
    /* fall back to regular malloc */
161
62.7k
    if (ret == NULL) {
162
0
        ret = *freeptr = OPENSSL_malloc(sizeof(struct ht_neighborhood_st) * len);
163
0
        if (ret == NULL)
164
0
            return NULL;
165
0
    }
166
62.7k
    memset(ret, 0, sizeof(struct ht_neighborhood_st) * len);
167
62.7k
    return ret;
168
62.7k
}
169
170
static void internal_free_nop(HT_VALUE *v)
171
24.5k
{
172
24.5k
    return;
173
24.5k
}
174
175
HT *ossl_ht_new(const HT_CONFIG *conf)
176
242
{
177
242
    HT *new = OPENSSL_zalloc(sizeof(*new));
178
179
242
    if (new == NULL)
180
0
        return NULL;
181
182
242
    new->atomic_lock = CRYPTO_THREAD_lock_new();
183
242
    if (new->atomic_lock == NULL)
184
0
        goto err;
185
186
242
    memcpy(&new->config, conf, sizeof(*conf));
187
188
242
    if (new->config.init_neighborhoods != 0) {
189
236
        new->wpd.neighborhood_len = new->config.init_neighborhoods;
190
        /* round up to the next power of 2 */
191
236
        new->wpd.neighborhood_len--;
192
236
        new->wpd.neighborhood_len |= new->wpd.neighborhood_len >> 1;
193
236
        new->wpd.neighborhood_len |= new->wpd.neighborhood_len >> 2;
194
236
        new->wpd.neighborhood_len |= new->wpd.neighborhood_len >> 4;
195
236
        new->wpd.neighborhood_len |= new->wpd.neighborhood_len >> 8;
196
236
        new->wpd.neighborhood_len |= new->wpd.neighborhood_len >> 16;
197
236
        new->wpd.neighborhood_len++;
198
236
    } else {
199
6
        new->wpd.neighborhood_len = DEFAULT_NEIGH_LEN;
200
6
    }
201
202
242
    if (new->config.ht_free_fn == NULL)
203
236
        new->config.ht_free_fn = internal_free_nop;
204
205
242
    new->md = OPENSSL_zalloc(sizeof(*new->md));
206
242
    if (new->md == NULL)
207
0
        goto err;
208
209
242
    new->md->neighborhoods = alloc_new_neighborhood_list(new->wpd.neighborhood_len,
210
242
        &new->md->neighborhood_ptr_to_free);
211
242
    if (new->md->neighborhoods == NULL)
212
0
        goto err;
213
242
    new->md->neighborhood_mask = new->wpd.neighborhood_len - 1;
214
215
242
    new->lock = ossl_rcu_lock_new(1, conf->ctx);
216
242
    if (new->lock == NULL)
217
0
        goto err;
218
219
242
    if (new->config.ht_hash_fn == NULL)
220
242
        new->config.ht_hash_fn = ossl_fnv1a_hash;
221
222
242
    return new;
223
224
0
err:
225
0
    CRYPTO_THREAD_lock_free(new->atomic_lock);
226
0
    ossl_rcu_lock_free(new->lock);
227
0
    if (new->md != NULL)
228
0
        OPENSSL_free(new->md->neighborhood_ptr_to_free);
229
0
    OPENSSL_free(new->md);
230
0
    OPENSSL_free(new);
231
0
    return NULL;
232
242
}
233
234
void ossl_ht_read_lock(HT *htable)
235
28
{
236
28
    ossl_rcu_read_lock(htable->lock);
237
28
}
238
239
void ossl_ht_read_unlock(HT *htable)
240
46
{
241
46
    ossl_rcu_read_unlock(htable->lock);
242
46
}
243
244
void ossl_ht_write_lock(HT *htable)
245
271
{
246
271
    ossl_rcu_write_lock(htable->lock);
247
271
    htable->wpd.need_sync = 0;
248
271
}
249
250
void ossl_ht_write_unlock(HT *htable)
251
271
{
252
271
    int need_sync = htable->wpd.need_sync;
253
254
271
    htable->wpd.need_sync = 0;
255
271
    ossl_rcu_write_unlock(htable->lock);
256
271
    if (need_sync)
257
181
        ossl_synchronize_rcu(htable->lock);
258
271
}
259
260
static void free_oldmd(void *arg)
261
31.2k
{
262
31.2k
    struct ht_mutable_data_st *oldmd = arg;
263
31.2k
    size_t i, j;
264
31.2k
    size_t neighborhood_len = (size_t)oldmd->neighborhood_mask + 1;
265
31.2k
    struct ht_internal_value_st *v;
266
267
359k
    for (i = 0; i < neighborhood_len; i++) {
268
328k
        PREFETCH_NEIGHBORHOOD(oldmd->neighborhoods[i + 1]);
269
1.64M
        for (j = 0; j < NEIGHBORHOOD_LEN; j++) {
270
1.31M
            if (oldmd->neighborhoods[i].entries[j].value != NULL) {
271
26.1k
                v = oldmd->neighborhoods[i].entries[j].value;
272
26.1k
                v->ht->config.ht_free_fn((HT_VALUE *)v);
273
26.1k
                free_value(v);
274
26.1k
            }
275
1.31M
        }
276
328k
    }
277
278
31.2k
    OPENSSL_free(oldmd->neighborhood_ptr_to_free);
279
31.2k
    OPENSSL_free(oldmd);
280
31.2k
}
281
282
static int ossl_ht_flush_internal(HT *h)
283
157
{
284
157
    struct ht_mutable_data_st *newmd = NULL;
285
157
    struct ht_mutable_data_st *oldmd = NULL;
286
287
157
    newmd = OPENSSL_zalloc(sizeof(*newmd));
288
157
    if (newmd == NULL)
289
0
        return 0;
290
291
157
    newmd->neighborhoods = alloc_new_neighborhood_list(DEFAULT_NEIGH_LEN,
292
157
        &newmd->neighborhood_ptr_to_free);
293
157
    if (newmd->neighborhoods == NULL) {
294
0
        OPENSSL_free(newmd);
295
0
        return 0;
296
0
    }
297
298
157
    newmd->neighborhood_mask = DEFAULT_NEIGH_LEN - 1;
299
300
    /* Swap the old and new mutable data sets */
301
157
    oldmd = ossl_rcu_deref(&h->md);
302
157
    ossl_rcu_assign_ptr(&h->md, &newmd);
303
304
    /* Set the number of entries to 0 */
305
157
    h->wpd.value_count = 0;
306
157
    h->wpd.neighborhood_len = DEFAULT_NEIGH_LEN;
307
308
157
    ossl_rcu_call(h->lock, free_oldmd, oldmd);
309
157
    h->wpd.need_sync = 1;
310
157
    return 1;
311
157
}
312
313
int ossl_ht_flush(HT *h)
314
4
{
315
4
    return ossl_ht_flush_internal(h);
316
4
}
317
318
void ossl_ht_free(HT *h)
319
154
{
320
154
    if (h == NULL)
321
0
        return;
322
323
154
    ossl_ht_write_lock(h);
324
154
    ossl_ht_flush_internal(h);
325
154
    ossl_ht_write_unlock(h);
326
    /* Freeing the lock does a final sync for us */
327
154
    CRYPTO_THREAD_lock_free(h->atomic_lock);
328
154
    ossl_rcu_lock_free(h->lock);
329
154
    OPENSSL_free(h->md->neighborhood_ptr_to_free);
330
154
    OPENSSL_free(h->md);
331
154
    OPENSSL_free(h);
332
154
    return;
333
154
}
334
335
size_t ossl_ht_count(HT *h)
336
0
{
337
0
    size_t count;
338
339
0
    count = h->wpd.value_count;
340
0
    return count;
341
0
}
342
343
void ossl_ht_foreach_until(HT *h, int (*cb)(HT_VALUE *obj, void *arg),
344
    void *arg)
345
74
{
346
74
    size_t i, j;
347
74
    struct ht_mutable_data_st *md;
348
349
74
    md = ossl_rcu_deref(&h->md);
350
11.8k
    for (i = 0; i < md->neighborhood_mask + 1; i++) {
351
11.7k
        PREFETCH_NEIGHBORHOOD(md->neighborhoods[i + 1]);
352
58.9k
        for (j = 0; j < NEIGHBORHOOD_LEN; j++) {
353
47.1k
            if (md->neighborhoods[i].entries[j].value != NULL) {
354
459
                if (!cb((HT_VALUE *)md->neighborhoods[i].entries[j].value, arg))
355
6
                    goto out;
356
459
            }
357
47.1k
        }
358
11.7k
    }
359
74
out:
360
74
    return;
361
74
}
362
363
HT_VALUE_LIST *ossl_ht_filter(HT *h, size_t max_len,
364
    int (*filter)(HT_VALUE *obj, void *arg),
365
    void *arg)
366
53
{
367
53
    struct ht_mutable_data_st *md;
368
53
    HT_VALUE_LIST *list = OPENSSL_zalloc(sizeof(HT_VALUE_LIST)
369
53
        + (sizeof(HT_VALUE *) * max_len));
370
53
    size_t i, j;
371
53
    struct ht_internal_value_st *v;
372
373
53
    if (list == NULL)
374
0
        return NULL;
375
376
    /*
377
     * The list array lives just beyond the end of
378
     * the struct
379
     */
380
53
    list->list = (HT_VALUE **)(list + 1);
381
382
53
    md = ossl_rcu_deref(&h->md);
383
7.01k
    for (i = 0; i < md->neighborhood_mask + 1; i++) {
384
6.96k
        PREFETCH_NEIGHBORHOOD(md->neighborhoods[i + 1]);
385
34.8k
        for (j = 0; j < NEIGHBORHOOD_LEN; j++) {
386
27.8k
            v = md->neighborhoods[i].entries[j].value;
387
27.8k
            if (v != NULL && filter((HT_VALUE *)v, arg)) {
388
1
                list->list[list->list_len++] = (HT_VALUE *)v;
389
1
                if (list->list_len == max_len)
390
1
                    goto out;
391
1
            }
392
27.8k
        }
393
6.96k
    }
394
53
out:
395
53
    return list;
396
53
}
397
398
void ossl_ht_value_list_free(HT_VALUE_LIST *list)
399
53
{
400
53
    OPENSSL_free(list);
401
53
}
402
403
static int compare_hash(uint64_t hash1, uint64_t hash2)
404
40.0M
{
405
40.0M
    return (hash1 == hash2);
406
40.0M
}
407
408
static void free_old_neigh_table(void *arg)
409
20
{
410
20
    struct ht_mutable_data_st *oldmd = arg;
411
412
20
    OPENSSL_free(oldmd->neighborhood_ptr_to_free);
413
20
    OPENSSL_free(oldmd);
414
20
}
415
416
/*
417
 * Increase hash table bucket list
418
 * must be called with write_lock held
419
 */
420
static int grow_hashtable(HT *h, size_t oldsize)
421
17
{
422
17
    struct ht_mutable_data_st *newmd;
423
17
    struct ht_mutable_data_st *oldmd = ossl_rcu_deref(&h->md);
424
17
    int rc = 0;
425
17
    uint64_t oldi, oldj, newi, newj;
426
17
    uint64_t oldhash;
427
17
    struct ht_internal_value_st *oldv;
428
17
    int rehashed;
429
17
    size_t newsize = oldsize * 2;
430
431
17
    if (h->config.lockless_reads)
432
0
        goto out;
433
434
17
    if ((newmd = OPENSSL_zalloc(sizeof(*newmd))) == NULL)
435
0
        goto out;
436
437
    /* bucket list is always a power of 2 */
438
17
    newmd->neighborhoods = alloc_new_neighborhood_list(oldsize * 2,
439
17
        &newmd->neighborhood_ptr_to_free);
440
17
    if (newmd->neighborhoods == NULL)
441
0
        goto out_free;
442
443
    /* being a power of 2 makes for easy mask computation */
444
17
    newmd->neighborhood_mask = (newsize - 1);
445
446
    /*
447
     * Now we need to start rehashing entries
448
     * Note we don't need to use atomics here as the new
449
     * mutable data hasn't been published
450
     */
451
2.51k
    for (oldi = 0; oldi < h->wpd.neighborhood_len; oldi++) {
452
2.49k
        PREFETCH_NEIGHBORHOOD(oldmd->neighborhoods[oldi + 1]);
453
12.4k
        for (oldj = 0; oldj < NEIGHBORHOOD_LEN; oldj++) {
454
9.98k
            oldv = oldmd->neighborhoods[oldi].entries[oldj].value;
455
9.98k
            if (oldv == NULL)
456
9.86k
                continue;
457
116
            oldhash = oldmd->neighborhoods[oldi].entries[oldj].hash;
458
116
            newi = oldhash & newmd->neighborhood_mask;
459
116
            rehashed = 0;
460
203
            for (newj = 0; newj < NEIGHBORHOOD_LEN; newj++) {
461
203
                if (newmd->neighborhoods[newi].entries[newj].value == NULL) {
462
116
                    newmd->neighborhoods[newi].entries[newj].value = oldv;
463
116
                    newmd->neighborhoods[newi].entries[newj].hash = oldhash;
464
116
                    rehashed = 1;
465
116
                    break;
466
116
                }
467
203
            }
468
116
            if (rehashed == 0) {
469
                /* we ran out of space in a neighborhood, grow again */
470
0
                OPENSSL_free(newmd->neighborhoods);
471
0
                OPENSSL_free(newmd);
472
0
                return grow_hashtable(h, newsize);
473
0
            }
474
116
        }
475
2.49k
    }
476
    /*
477
     * Now that our entries are all hashed into the new bucket list
478
     * update our bucket_len and target_max_load
479
     */
480
17
    h->wpd.neighborhood_len = newsize;
481
482
    /*
483
     * Now we replace the old mutable data with the new
484
     */
485
17
    ossl_rcu_assign_ptr(&h->md, &newmd);
486
17
    ossl_rcu_call(h->lock, free_old_neigh_table, oldmd);
487
17
    h->wpd.need_sync = 1;
488
    /*
489
     * And we're done
490
     */
491
17
    rc = 1;
492
493
17
out:
494
17
    return rc;
495
0
out_free:
496
0
    OPENSSL_free(newmd->neighborhoods);
497
0
    OPENSSL_free(newmd);
498
0
    goto out;
499
17
}
500
501
static void free_old_ht_value(void *arg)
502
9
{
503
9
    HT_VALUE *h = (HT_VALUE *)arg;
504
505
    /*
506
     * Note, this is only called on replacement,
507
     * the caller is responsible for freeing the
508
     * held data, we just need to free the wrapping
509
     * struct here
510
     */
511
9
    OPENSSL_free(h);
512
9
}
513
514
static ossl_inline int match_key(HT_KEY *a, HT_KEY *b)
515
30.6M
{
516
    /*
517
     * keys match if they are both present, the same size
518
     * and compare equal in memory
519
     */
520
30.6M
    PREFETCH(a->keybuf);
521
30.6M
    PREFETCH(b->keybuf);
522
30.6M
    if (a->keybuf != NULL && b->keybuf != NULL && a->keysize == b->keysize)
523
30.6M
        return !memcmp(a->keybuf, b->keybuf, a->keysize);
524
525
2.51k
    return 1;
526
30.6M
}
527
528
static int ossl_ht_insert_locked(HT *h, uint64_t hash,
529
    struct ht_internal_value_st *newval,
530
    HT_VALUE **olddata)
531
28.2k
{
532
28.2k
    struct ht_mutable_data_st *md = h->md;
533
28.2k
    uint64_t neigh_idx_start = hash & md->neighborhood_mask;
534
28.2k
    uint64_t neigh_idx = neigh_idx_start;
535
28.2k
    size_t j;
536
28.2k
    uint64_t ihash;
537
28.2k
    HT_VALUE *ival;
538
28.2k
    size_t empty_idx = SIZE_MAX;
539
28.2k
    int lockless_reads = h->config.lockless_reads;
540
541
28.5k
    do {
542
28.5k
        PREFETCH_NEIGHBORHOOD(md->neighborhoods[neigh_idx]);
543
544
43.1k
        for (j = 0; j < NEIGHBORHOOD_LEN; j++) {
545
42.7k
            ival = ossl_rcu_deref(&md->neighborhoods[neigh_idx].entries[j].value);
546
42.7k
            if (ival == NULL) {
547
28.3k
                empty_idx = j;
548
                /* lockless_reads implies no deletion, we can break out */
549
28.3k
                if (lockless_reads)
550
28.1k
                    goto not_found;
551
181
                continue;
552
28.3k
            }
553
14.4k
            if (!CRYPTO_atomic_load(&md->neighborhoods[neigh_idx].entries[j].hash,
554
14.4k
                    &ihash, h->atomic_lock))
555
0
                return 0;
556
14.4k
            if (compare_hash(hash, ihash) && match_key(&newval->value.key, &ival->key)) {
557
12
                if (olddata == NULL) {
558
                    /* This would insert a duplicate -> fail */
559
7
                    return 0;
560
7
                }
561
                /* Do a replacement */
562
5
                if (!CRYPTO_atomic_store(&md->neighborhoods[neigh_idx].entries[j].hash,
563
5
                        hash, h->atomic_lock))
564
0
                    return 0;
565
5
                *olddata = (HT_VALUE *)md->neighborhoods[neigh_idx].entries[j].value;
566
5
                ossl_rcu_assign_ptr(&md->neighborhoods[neigh_idx].entries[j].value,
567
5
                    &newval);
568
5
                ossl_rcu_call(h->lock, free_old_ht_value, *olddata);
569
5
                h->wpd.need_sync = 1;
570
5
                return 1;
571
5
            }
572
14.4k
        }
573
343
        if (!lockless_reads)
574
78
            break;
575
        /* Continue search in subsequent neighborhoods */
576
265
        neigh_idx = (neigh_idx + 1) & md->neighborhood_mask;
577
265
    } while (neigh_idx != neigh_idx_start);
578
579
28.2k
not_found:
580
    /* If we get to here, its just an insert */
581
28.2k
    if (empty_idx == SIZE_MAX)
582
17
        return -1; /* out of space */
583
28.2k
    if (!CRYPTO_atomic_store(&md->neighborhoods[neigh_idx].entries[empty_idx].hash,
584
28.2k
            hash, h->atomic_lock))
585
0
        return 0;
586
28.2k
    h->wpd.value_count++;
587
28.2k
    ossl_rcu_assign_ptr(&md->neighborhoods[neigh_idx].entries[empty_idx].value,
588
28.2k
        &newval);
589
28.2k
    return 1;
590
28.2k
}
591
592
static struct ht_internal_value_st *alloc_new_value(HT *h, HT_KEY *key,
593
    void *data,
594
    uintptr_t *type)
595
40.4k
{
596
40.4k
    struct ht_internal_value_st *tmp;
597
40.4k
    size_t nvsize = sizeof(*tmp);
598
599
40.4k
    if (h->config.collision_check == 1)
600
38.9k
        nvsize += key->keysize;
601
602
40.4k
    tmp = OPENSSL_malloc(nvsize);
603
604
40.4k
    if (tmp == NULL)
605
0
        return NULL;
606
607
40.4k
    tmp->ht = h;
608
40.4k
    tmp->value.value = data;
609
40.4k
    tmp->value.type_id = type;
610
40.4k
    tmp->value.key.keybuf = NULL;
611
40.4k
    if (h->config.collision_check) {
612
38.9k
        tmp->value.key.keybuf = (uint8_t *)(tmp + 1);
613
38.9k
        tmp->value.key.keysize = key->keysize;
614
38.9k
        memcpy(tmp->value.key.keybuf, key->keybuf, key->keysize);
615
38.9k
    }
616
617
40.4k
    return tmp;
618
40.4k
}
619
620
static void free_value(struct ht_internal_value_st *v)
621
26.1k
{
622
26.1k
    OPENSSL_free(v);
623
26.1k
}
624
625
int ossl_ht_insert(HT *h, HT_KEY *key, HT_VALUE *data, HT_VALUE **olddata)
626
40.4k
{
627
40.4k
    struct ht_internal_value_st *newval = NULL;
628
40.4k
    uint64_t hash;
629
40.4k
    int rc = 0;
630
40.4k
    int i;
631
632
40.4k
    if (data->value == NULL)
633
0
        goto out;
634
635
40.4k
    newval = alloc_new_value(h, key, data->value, data->type_id);
636
40.4k
    if (newval == NULL)
637
0
        goto out;
638
639
    /*
640
     * we have to take our lock here to prevent other changes
641
     * to the bucket list
642
     */
643
40.4k
    hash = h->config.ht_hash_fn(key->keybuf, key->keysize);
644
645
40.4k
    for (i = 0;
646
40.4k
        (rc = ossl_ht_insert_locked(h, hash, newval, olddata)) == -1
647
20
        && i < 4;
648
40.4k
        ++i)
649
20
        if (!grow_hashtable(h, h->wpd.neighborhood_len)) {
650
0
            rc = -1;
651
0
            break;
652
0
        }
653
654
40.4k
    if (rc <= 0)
655
11
        free_value(newval);
656
657
40.4k
out:
658
40.4k
    return rc;
659
40.4k
}
660
661
HT_VALUE *ossl_ht_get(HT *h, HT_KEY *key)
662
24.6M
{
663
24.6M
    struct ht_mutable_data_st *md;
664
24.6M
    uint64_t hash;
665
24.6M
    uint64_t neigh_idx_start;
666
24.6M
    uint64_t neigh_idx;
667
24.6M
    struct ht_internal_value_st *ival = NULL;
668
24.6M
    size_t j;
669
24.6M
    uint64_t ehash;
670
24.6M
    int lockless_reads = h->config.lockless_reads;
671
672
24.6M
    hash = h->config.ht_hash_fn(key->keybuf, key->keysize);
673
674
24.6M
    md = ossl_rcu_deref(&h->md);
675
24.6M
    neigh_idx = neigh_idx_start = hash & md->neighborhood_mask;
676
24.6M
    do {
677
24.6M
        PREFETCH_NEIGHBORHOOD(md->neighborhoods[neigh_idx]);
678
31.6M
        for (j = 0; j < NEIGHBORHOOD_LEN; j++) {
679
31.6M
            ival = ossl_rcu_deref(&md->neighborhoods[neigh_idx].entries[j].value);
680
31.6M
            if (ival == NULL) {
681
2.27M
                if (lockless_reads)
682
                    /* lockless_reads implies no deletion, we can break out */
683
2.27M
                    return NULL;
684
98
                continue;
685
2.27M
            }
686
29.3M
            if (!CRYPTO_atomic_load(&md->neighborhoods[neigh_idx].entries[j].hash,
687
29.3M
                    &ehash, h->atomic_lock))
688
0
                return NULL;
689
29.3M
            if (compare_hash(hash, ehash) && match_key(&ival->value.key, key))
690
22.4M
                return (HT_VALUE *)ival;
691
29.3M
        }
692
4.44k
        if (!lockless_reads)
693
41
            break;
694
        /* Continue search in subsequent neighborhoods */
695
4.39k
        neigh_idx = (neigh_idx + 1) & md->neighborhood_mask;
696
4.39k
    } while (neigh_idx != neigh_idx_start);
697
698
41
    return NULL;
699
24.6M
}
700
701
static void free_old_entry(void *arg)
702
13
{
703
13
    struct ht_internal_value_st *v = arg;
704
705
13
    v->ht->config.ht_free_fn((HT_VALUE *)v);
706
13
    free_value(v);
707
13
}
708
709
int ossl_ht_delete(HT *h, HT_KEY *key)
710
41
{
711
41
    uint64_t hash;
712
41
    uint64_t neigh_idx;
713
41
    size_t j;
714
41
    struct ht_internal_value_st *v = NULL;
715
41
    HT_VALUE *nv = NULL;
716
41
    int rc = 0;
717
718
41
    if (h->config.lockless_reads)
719
0
        return 0;
720
721
41
    hash = h->config.ht_hash_fn(key->keybuf, key->keysize);
722
723
41
    neigh_idx = hash & h->md->neighborhood_mask;
724
41
    PREFETCH_NEIGHBORHOOD(h->md->neighborhoods[neigh_idx]);
725
184
    for (j = 0; j < NEIGHBORHOOD_LEN; j++) {
726
152
        v = (struct ht_internal_value_st *)h->md->neighborhoods[neigh_idx].entries[j].value;
727
152
        if (v == NULL)
728
78
            continue;
729
74
        if (compare_hash(hash, h->md->neighborhoods[neigh_idx].entries[j].hash)
730
9
            && match_key(key, &v->value.key)) {
731
9
            if (!CRYPTO_atomic_store(&h->md->neighborhoods[neigh_idx].entries[j].hash,
732
9
                    0, h->atomic_lock))
733
0
                break;
734
9
            h->wpd.value_count--;
735
9
            ossl_rcu_assign_ptr(&h->md->neighborhoods[neigh_idx].entries[j].value,
736
9
                &nv);
737
9
            rc = 1;
738
9
            break;
739
9
        }
740
74
    }
741
41
    if (rc == 1) {
742
9
        ossl_rcu_call(h->lock, free_old_entry, v);
743
9
        h->wpd.need_sync = 1;
744
9
    }
745
41
    return rc;
746
41
}