Coverage Report

Created: 2026-01-07 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/property/property.c
Line
Count
Source
1
/*
2
 * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
3
 * Copyright (c) 2019, Oracle and/or its affiliates.  All rights reserved.
4
 *
5
 * Licensed under the Apache License 2.0 (the "License").  You may not use
6
 * this file except in compliance with the License.  You can obtain a copy
7
 * in the file LICENSE in the source distribution or at
8
 * https://www.openssl.org/source/license.html
9
 */
10
11
#include <string.h>
12
#include <stdio.h>
13
#include <stdarg.h>
14
#include <openssl/crypto.h>
15
#include "internal/property.h"
16
#include "internal/provider.h"
17
#include "internal/tsan_assist.h"
18
#include <openssl/lhash.h>
19
#include <openssl/rand.h>
20
#include <openssl/trace.h>
21
#include "crypto/sparse_array.h"
22
#include "property_local.h"
23
#include "crypto/context.h"
24
25
/*
26
 * The shard count was determined through performance testing with the evp_fetch
27
 * tool on an Intel Xeon Gold 6248R CPU @ 3.00GHz. Testing showed that 4 shards
28
 * combined with CACHE_SIZE delivered the best performance for 16 or
29
 * more threads, and close to best performance at below 16 threads.
30
 */
31
#ifndef NUM_SHARDS
32
786k
#define NUM_SHARDS 4
33
#endif
34
35
#ifndef CACHE_SIZE
36
23
#define CACHE_SIZE 512
37
#endif
38
39
/*
40
 * The number of elements in the query cache before we initiate a flush.
41
 * If reducing this, also ensure the stochastic test in test/property_test.c
42
 * isn't likely to fail.
43
 */
44
23
#define IMPL_CACHE_FLUSH_THRESHOLD (CACHE_SIZE / NUM_SHARDS)
45
46
typedef struct {
47
    void *method;
48
    int (*up_ref)(void *);
49
    void (*free)(void *);
50
} METHOD;
51
52
typedef struct {
53
    const OSSL_PROVIDER *provider;
54
    OSSL_PROPERTY_LIST *properties;
55
    METHOD method;
56
} IMPLEMENTATION;
57
58
DEFINE_STACK_OF(IMPLEMENTATION)
59
60
typedef struct {
61
    const OSSL_PROVIDER *provider;
62
    const char *query;
63
    METHOD method;
64
    char body[1];
65
} QUERY;
66
67
DEFINE_LHASH_OF_EX(QUERY);
68
69
typedef struct {
70
    int nid;
71
    STACK_OF(IMPLEMENTATION) *impls;
72
    LHASH_OF(QUERY) *cache;
73
} ALGORITHM;
74
75
typedef struct {
76
    SPARSE_ARRAY_OF(ALGORITHM) * algs;
77
78
    /*
79
     * Lock to protect each shard of |algs| from concurrent writing,
80
     * when individual implementations or queries are inserted.  This is used
81
     * by the appropriate functions here.
82
     */
83
    CRYPTO_RWLOCK *lock;
84
85
    /* query cache specific values */
86
87
    /* Count of the query cache entries for all algs */
88
    size_t cache_nelem;
89
90
    /* Flag: 1 if query cache entries for all algs need flushing */
91
    int cache_need_flush;
92
} STORED_ALGORITHMS;
93
94
struct ossl_method_store_st {
95
    OSSL_LIB_CTX *ctx;
96
    STORED_ALGORITHMS *algs;
97
    /*
98
     * Lock to reserve the whole store.  This is used when fetching a set
99
     * of algorithms, via these functions, found in crypto/core_fetch.c:
100
     * ossl_method_construct_reserve_store()
101
     * ossl_method_construct_unreserve_store()
102
     */
103
    CRYPTO_RWLOCK *biglock;
104
};
105
106
typedef struct {
107
    LHASH_OF(QUERY) *cache;
108
    size_t nelem;
109
    uint32_t seed;
110
    unsigned char using_global_seed;
111
} IMPL_CACHE_FLUSH;
112
113
DEFINE_SPARSE_ARRAY_OF(ALGORITHM);
114
115
DEFINE_STACK_OF(ALGORITHM)
116
117
typedef struct ossl_global_properties_st {
118
    OSSL_PROPERTY_LIST *list;
119
#ifndef FIPS_MODULE
120
    unsigned int no_mirrored : 1;
121
#endif
122
} OSSL_GLOBAL_PROPERTIES;
123
124
786k
#define stored_algs_shard(store, nid) (&(store)->algs[(nid) & (NUM_SHARDS - 1)])
125
126
static void ossl_method_cache_flush_alg(STORED_ALGORITHMS *sa,
127
    ALGORITHM *alg);
128
static void ossl_method_cache_flush(STORED_ALGORITHMS *sa, int nid);
129
130
/* Global properties are stored per library context */
131
void ossl_ctx_global_properties_free(void *vglobp)
132
0
{
133
0
    OSSL_GLOBAL_PROPERTIES *globp = vglobp;
134
135
0
    if (globp != NULL) {
136
0
        ossl_property_free(globp->list);
137
0
        OPENSSL_free(globp);
138
0
    }
139
0
}
140
141
void *ossl_ctx_global_properties_new(OSSL_LIB_CTX *ctx)
142
3
{
143
3
    return OPENSSL_zalloc(sizeof(OSSL_GLOBAL_PROPERTIES));
144
3
}
145
146
OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *libctx,
147
    ossl_unused int loadconfig)
148
239
{
149
239
    OSSL_GLOBAL_PROPERTIES *globp;
150
151
239
#if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_AUTOLOAD_CONFIG)
152
239
    if (loadconfig && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
153
0
        return NULL;
154
239
#endif
155
239
    globp = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES);
156
157
239
    return globp != NULL ? &globp->list : NULL;
158
239
}
159
160
#ifndef FIPS_MODULE
161
int ossl_global_properties_no_mirrored(OSSL_LIB_CTX *libctx)
162
1
{
163
1
    OSSL_GLOBAL_PROPERTIES *globp
164
1
        = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES);
165
166
1
    return globp != NULL && globp->no_mirrored ? 1 : 0;
167
1
}
168
169
void ossl_global_properties_stop_mirroring(OSSL_LIB_CTX *libctx)
170
0
{
171
0
    OSSL_GLOBAL_PROPERTIES *globp
172
0
        = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES);
173
174
0
    if (globp != NULL)
175
0
        globp->no_mirrored = 1;
176
0
}
177
#endif
178
179
static int ossl_method_up_ref(METHOD *method)
180
786k
{
181
786k
    return (*method->up_ref)(method->method);
182
786k
}
183
184
static void ossl_method_free(METHOD *method)
185
0
{
186
0
    (*method->free)(method->method);
187
0
}
188
189
static __owur int ossl_property_read_lock(STORED_ALGORITHMS *p)
190
786k
{
191
786k
    return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0;
192
786k
}
193
194
static __owur int ossl_property_write_lock(STORED_ALGORITHMS *p)
195
122
{
196
122
    return p != NULL ? CRYPTO_THREAD_write_lock(p->lock) : 0;
197
122
}
198
199
static int ossl_property_unlock(STORED_ALGORITHMS *p)
200
786k
{
201
786k
    return p != 0 ? CRYPTO_THREAD_unlock(p->lock) : 0;
202
786k
}
203
204
static unsigned long query_hash(const QUERY *a)
205
786k
{
206
786k
    return OPENSSL_LH_strhash(a->query);
207
786k
}
208
209
static int query_cmp(const QUERY *a, const QUERY *b)
210
786k
{
211
786k
    int res = strcmp(a->query, b->query);
212
213
786k
    if (res == 0 && a->provider != NULL && b->provider != NULL)
214
0
        res = b->provider > a->provider ? 1
215
0
            : b->provider < a->provider ? -1
216
0
                                        : 0;
217
786k
    return res;
218
786k
}
219
220
static void impl_free(IMPLEMENTATION *impl)
221
0
{
222
0
    if (impl != NULL) {
223
0
        ossl_method_free(&impl->method);
224
0
        OPENSSL_free(impl);
225
0
    }
226
0
}
227
228
static void impl_cache_free(QUERY *elem)
229
0
{
230
0
    if (elem != NULL) {
231
0
        ossl_method_free(&elem->method);
232
0
        OPENSSL_free(elem);
233
0
    }
234
0
}
235
236
static void impl_cache_flush_alg(ossl_uintmax_t idx, ALGORITHM *alg)
237
1
{
238
1
    lh_QUERY_doall(alg->cache, &impl_cache_free);
239
1
    lh_QUERY_flush(alg->cache);
240
1
}
241
242
static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a, void *arg)
243
0
{
244
0
    STORED_ALGORITHMS *sa = arg;
245
246
0
    if (a != NULL) {
247
0
        sk_IMPLEMENTATION_pop_free(a->impls, &impl_free);
248
0
        lh_QUERY_doall(a->cache, &impl_cache_free);
249
0
        lh_QUERY_free(a->cache);
250
0
        OPENSSL_free(a);
251
0
    }
252
0
    if (sa != NULL)
253
0
        ossl_sa_ALGORITHM_set(sa->algs, idx, NULL);
254
0
}
255
256
static void stored_algs_free(STORED_ALGORITHMS *sa)
257
0
{
258
0
    if (sa == NULL)
259
0
        return;
260
261
0
    for (int i = 0; i < NUM_SHARDS; ++i) {
262
0
        ossl_sa_ALGORITHM_doall_arg(sa[i].algs, &alg_cleanup, &sa[i]);
263
0
        ossl_sa_ALGORITHM_free(sa[i].algs);
264
0
        CRYPTO_THREAD_lock_free(sa[i].lock);
265
0
    }
266
267
0
    OPENSSL_free(sa);
268
0
}
269
270
static STORED_ALGORITHMS *stored_algs_new(void)
271
12
{
272
12
    STORED_ALGORITHMS *ret;
273
274
12
    ret = OPENSSL_calloc(NUM_SHARDS, sizeof(STORED_ALGORITHMS));
275
12
    if (ret == NULL)
276
0
        return NULL;
277
278
60
    for (int i = 0; i < NUM_SHARDS; ++i) {
279
48
        ret[i].algs = ossl_sa_ALGORITHM_new();
280
48
        if (ret[i].algs == NULL)
281
0
            goto err;
282
283
48
        ret[i].lock = CRYPTO_THREAD_lock_new();
284
48
        if (ret[i].lock == NULL)
285
0
            goto err;
286
48
    }
287
288
12
    return ret;
289
290
0
err:
291
0
    stored_algs_free(ret);
292
293
0
    return NULL;
294
12
}
295
296
/*
297
 * The OSSL_LIB_CTX param here allows access to underlying property data needed
298
 * for computation
299
 */
300
OSSL_METHOD_STORE *ossl_method_store_new(OSSL_LIB_CTX *ctx)
301
12
{
302
12
    OSSL_METHOD_STORE *res;
303
304
12
    res = OPENSSL_zalloc(sizeof(*res));
305
12
    if (res != NULL) {
306
12
        res->ctx = ctx;
307
12
        if ((res->algs = stored_algs_new()) == NULL
308
12
            || (res->biglock = CRYPTO_THREAD_lock_new()) == NULL) {
309
0
            ossl_method_store_free(res);
310
0
            return NULL;
311
0
        }
312
12
    }
313
12
    return res;
314
12
}
315
316
void ossl_method_store_free(OSSL_METHOD_STORE *store)
317
0
{
318
0
    if (store == NULL)
319
0
        return;
320
321
0
    stored_algs_free(store->algs);
322
0
    CRYPTO_THREAD_lock_free(store->biglock);
323
0
    OPENSSL_free(store);
324
0
}
325
326
int ossl_method_lock_store(OSSL_METHOD_STORE *store)
327
1.15k
{
328
1.15k
    return store != NULL ? CRYPTO_THREAD_write_lock(store->biglock) : 0;
329
1.15k
}
330
331
int ossl_method_unlock_store(OSSL_METHOD_STORE *store)
332
1.15k
{
333
1.15k
    return store != NULL ? CRYPTO_THREAD_unlock(store->biglock) : 0;
334
1.15k
}
335
336
static ALGORITHM *ossl_method_store_retrieve(STORED_ALGORITHMS *sa, int nid)
337
786k
{
338
786k
    return ossl_sa_ALGORITHM_get(sa->algs, nid);
339
786k
}
340
341
static int ossl_method_store_insert(STORED_ALGORITHMS *sa, ALGORITHM *alg)
342
30
{
343
30
    return ossl_sa_ALGORITHM_set(sa->algs, alg->nid, alg);
344
30
}
345
346
/**
347
 * @brief Adds a method to the specified method store.
348
 *
349
 * This function adds a new method to the provided method store, associating it
350
 * with a specified id, properties, and provider. The method is stored with
351
 * reference count and destruction callbacks.
352
 *
353
 * @param store Pointer to the OSSL_METHOD_STORE where the method will be added.
354
 *              Must be non-null.
355
 * @param prov Pointer to the OSSL_PROVIDER for the provider of the method.
356
 *             Must be non-null.
357
 * @param nid (identifier) associated with the method, must be > 0
358
 * @param properties String containing properties of the method.
359
 * @param method Pointer to the method to be added.
360
 * @param method_up_ref Function pointer for incrementing the method ref count.
361
 * @param method_destruct Function pointer for destroying the method.
362
 *
363
 * @return 1 if the method is successfully added, 0 on failure.
364
 *
365
 * If tracing is enabled, a message is printed indicating that the method is
366
 * being added to the method store.
367
 *
368
 * NOTE: The nid parameter here is _not_ a nid in the sense of the NID_* macros.
369
 * It is an internal unique identifier.
370
 */
371
int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
372
    int nid, const char *properties, void *method,
373
    int (*method_up_ref)(void *),
374
    void (*method_destruct)(void *))
375
31
{
376
31
    STORED_ALGORITHMS *sa;
377
31
    ALGORITHM *alg = NULL;
378
31
    IMPLEMENTATION *impl;
379
31
    int ret = 0;
380
31
    int i;
381
382
31
    if (nid <= 0 || method == NULL || store == NULL)
383
0
        return 0;
384
385
31
    if (properties == NULL)
386
0
        properties = "";
387
388
31
    if (!ossl_assert(prov != NULL))
389
0
        return 0;
390
391
    /* Create new entry */
392
31
    impl = OPENSSL_malloc(sizeof(*impl));
393
31
    if (impl == NULL)
394
0
        return 0;
395
31
    impl->method.method = method;
396
31
    impl->method.up_ref = method_up_ref;
397
31
    impl->method.free = method_destruct;
398
31
    if (!ossl_method_up_ref(&impl->method)) {
399
0
        OPENSSL_free(impl);
400
0
        return 0;
401
0
    }
402
31
    impl->provider = prov;
403
404
31
    sa = stored_algs_shard(store, nid);
405
406
    /* Insert into the hash table if required */
407
31
    if (!ossl_property_write_lock(sa)) {
408
0
        impl_free(impl);
409
0
        return 0;
410
0
    }
411
412
    /*
413
     * Flush the alg cache of any implementation that already exists
414
     * for this id.
415
     * This is done to ensure that on the next lookup we go through the
416
     * provider comparison in ossl_method_store_fetch.  If we don't do this
417
     * then this new method won't be given a chance to get selected.
418
     * NOTE: This doesn't actually remove the method from the backing store
419
     * It just ensures that we query the backing store when (re)-adding a
420
     * method to the algorithm cache, in case the one selected by the next
421
     * query selects a different implementation
422
     */
423
31
    ossl_method_cache_flush(sa, nid);
424
425
    /*
426
     * Parse the properties associated with this method, and convert it to a
427
     * property list stored against the implementation for later comparison
428
     * during fetch operations
429
     */
430
31
    if ((impl->properties = ossl_prop_defn_get(store->ctx, properties)) == NULL) {
431
2
        impl->properties = ossl_parse_property(store->ctx, properties);
432
2
        if (impl->properties == NULL)
433
0
            goto err;
434
2
        if (!ossl_prop_defn_set(store->ctx, properties, &impl->properties)) {
435
0
            ossl_property_free(impl->properties);
436
0
            impl->properties = NULL;
437
0
            goto err;
438
0
        }
439
2
    }
440
441
    /*
442
     * Check if we have an algorithm cache already for this nid.  If so use
443
     * it, otherwise, create it, and insert it into the store
444
     */
445
31
    alg = ossl_method_store_retrieve(sa, nid);
446
31
    if (alg == NULL) {
447
30
        if ((alg = OPENSSL_zalloc(sizeof(*alg))) == NULL
448
30
            || (alg->impls = sk_IMPLEMENTATION_new_null()) == NULL
449
30
            || (alg->cache = lh_QUERY_new(&query_hash, &query_cmp)) == NULL)
450
0
            goto err;
451
30
        alg->nid = nid;
452
30
        if (!ossl_method_store_insert(sa, alg))
453
0
            goto err;
454
30
        OSSL_TRACE2(QUERY, "Inserted an alg with nid %d into the stored algorithms %p\n",
455
30
            nid, (void *)sa);
456
30
    }
457
458
    /* Push onto stack if there isn't one there already */
459
32
    for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
460
1
        const IMPLEMENTATION *tmpimpl = sk_IMPLEMENTATION_value(alg->impls, i);
461
462
1
        if (tmpimpl->provider == impl->provider
463
0
            && tmpimpl->properties == impl->properties)
464
0
            break;
465
1
    }
466
467
31
    if (i == sk_IMPLEMENTATION_num(alg->impls)
468
31
        && sk_IMPLEMENTATION_push(alg->impls, impl)) {
469
31
        ret = 1;
470
31
#ifndef FIPS_MODULE
471
31
        OSSL_TRACE_BEGIN(QUERY)
472
0
        {
473
0
            BIO_printf(trc_out, "Adding to method store "
474
0
                                "nid: %d\nproperties: %s\nprovider: %s\n",
475
0
                nid, properties,
476
0
                ossl_provider_name(prov) == NULL ? "none" : ossl_provider_name(prov));
477
0
        }
478
31
        OSSL_TRACE_END(QUERY);
479
31
#endif
480
31
    }
481
31
    ossl_property_unlock(sa);
482
31
    if (ret == 0)
483
0
        impl_free(impl);
484
31
    return ret;
485
486
0
err:
487
0
    ossl_property_unlock(sa);
488
0
    alg_cleanup(0, alg, NULL);
489
0
    impl_free(impl);
490
0
    return 0;
491
31
}
492
493
int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
494
    const void *method)
495
0
{
496
0
    ALGORITHM *alg = NULL;
497
0
    STORED_ALGORITHMS *sa;
498
0
    int i;
499
500
0
    if (nid <= 0 || method == NULL || store == NULL)
501
0
        return 0;
502
503
0
    sa = stored_algs_shard(store, nid);
504
0
    if (!ossl_property_write_lock(sa))
505
0
        return 0;
506
0
    ossl_method_cache_flush(sa, nid);
507
0
    alg = ossl_method_store_retrieve(sa, nid);
508
0
    if (alg == NULL) {
509
0
        ossl_property_unlock(sa);
510
0
        return 0;
511
0
    }
512
513
    /*
514
     * A sorting find then a delete could be faster but these stacks should be
515
     * relatively small, so we avoid the overhead.  Sorting could also surprise
516
     * users when result orderings change (even though they are not guaranteed).
517
     */
518
0
    for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
519
0
        IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
520
521
0
        if (impl->method.method == method) {
522
0
            impl_free(impl);
523
0
            (void)sk_IMPLEMENTATION_delete(alg->impls, i);
524
0
            ossl_property_unlock(sa);
525
0
            return 1;
526
0
        }
527
0
    }
528
0
    ossl_property_unlock(sa);
529
0
    return 0;
530
0
}
531
532
struct alg_cleanup_by_provider_data_st {
533
    STORED_ALGORITHMS *sa;
534
    const OSSL_PROVIDER *prov;
535
};
536
537
/**
538
 * @brief Cleans up implementations of an algorithm associated with a provider.
539
 *
540
 * This function removes all implementations of a specified algorithm that are
541
 * associated with a given provider. The function walks through the stack of
542
 * implementations backwards to handle deletions without affecting indexing.
543
 *
544
 * @param idx Index of the algorithm (unused in this function).
545
 * @param alg Pointer to the ALGORITHM structure containing the implementations.
546
 * @param arg Pointer to the data containing the provider information.
547
 *
548
 * If tracing is enabled, messages are printed indicating the removal of each
549
 * implementation and its properties. If any implementation is removed, the
550
 * associated cache is flushed.
551
 */
552
static void
553
alg_cleanup_by_provider(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
554
0
{
555
0
    struct alg_cleanup_by_provider_data_st *data = arg;
556
0
    int i, count;
557
558
    /*
559
     * We walk the stack backwards, to avoid having to deal with stack shifts
560
     * caused by deletion
561
     */
562
0
    for (count = 0, i = sk_IMPLEMENTATION_num(alg->impls); i-- > 0;) {
563
0
        IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
564
565
0
        if (impl->provider == data->prov) {
566
0
#ifndef FIPS_MODULE
567
0
            OSSL_TRACE_BEGIN(QUERY)
568
0
            {
569
0
                char buf[512];
570
0
                size_t size;
571
572
0
                size = ossl_property_list_to_string(NULL, impl->properties, buf,
573
0
                    sizeof(buf));
574
0
                BIO_printf(trc_out, "Removing implementation from "
575
0
                                    "query cache\nproperties %s\nprovider %s\n",
576
0
                    size == 0 ? "none" : buf,
577
0
                    ossl_provider_name(impl->provider) == NULL ? "none" : ossl_provider_name(impl->provider));
578
0
            }
579
0
            OSSL_TRACE_END(QUERY);
580
0
#endif
581
582
0
            (void)sk_IMPLEMENTATION_delete(alg->impls, i);
583
0
            count++;
584
0
            impl_free(impl);
585
0
        }
586
0
    }
587
588
    /*
589
     * If we removed any implementation, we also clear the whole associated
590
     * cache, 'cause that's the sensible thing to do.
591
     * There's no point flushing the cache entries where we didn't remove
592
     * any implementation, though.
593
     */
594
0
    if (count > 0)
595
0
        ossl_method_cache_flush_alg(data->sa, alg);
596
0
}
597
598
int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store,
599
    const OSSL_PROVIDER *prov)
600
0
{
601
0
    struct alg_cleanup_by_provider_data_st data;
602
603
0
    for (int k = 0; k < NUM_SHARDS; ++k) {
604
0
        STORED_ALGORITHMS *sa = &store->algs[k];
605
606
0
        if (!ossl_property_write_lock(sa))
607
0
            return 0;
608
0
        data.prov = prov;
609
0
        data.sa = sa;
610
0
        ossl_sa_ALGORITHM_doall_arg(sa->algs, &alg_cleanup_by_provider, &data);
611
0
        ossl_property_unlock(sa);
612
0
    }
613
0
    return 1;
614
0
}
615
616
static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl,
617
    void (*fn)(int id, void *method, void *fnarg),
618
    void *fnarg)
619
0
{
620
0
    fn(alg->nid, impl->method.method, fnarg);
621
0
}
622
623
static void alg_copy(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
624
0
{
625
0
    STACK_OF(ALGORITHM) *newalg = arg;
626
627
0
    alg = OPENSSL_memdup(alg, sizeof(ALGORITHM));
628
0
    if (alg == NULL)
629
0
        return;
630
631
0
    alg->impls = sk_IMPLEMENTATION_dup(alg->impls);
632
633
0
    (void)sk_ALGORITHM_push(newalg, alg);
634
0
}
635
636
static void del_tmpalg(ALGORITHM *alg)
637
0
{
638
0
    sk_IMPLEMENTATION_free(alg->impls);
639
0
    OPENSSL_free(alg);
640
0
}
641
642
void ossl_method_store_do_all(OSSL_METHOD_STORE *store,
643
    void (*fn)(int id, void *method, void *fnarg),
644
    void *fnarg)
645
0
{
646
0
    int i, j;
647
0
    int numalgs, numimps;
648
0
    STACK_OF(ALGORITHM) *tmpalgs;
649
0
    ALGORITHM *alg;
650
651
0
    if (store == NULL)
652
0
        return;
653
654
0
    for (int k = 0; k < NUM_SHARDS; ++k) {
655
0
        STORED_ALGORITHMS *sa = &store->algs[k];
656
657
0
        if (!ossl_property_read_lock(sa))
658
0
            return;
659
660
0
        tmpalgs = sk_ALGORITHM_new_reserve(NULL,
661
0
            (int)ossl_sa_ALGORITHM_num(sa->algs));
662
0
        if (tmpalgs == NULL) {
663
0
            ossl_property_unlock(sa);
664
0
            return;
665
0
        }
666
667
0
        ossl_sa_ALGORITHM_doall_arg(sa->algs, alg_copy, tmpalgs);
668
0
        ossl_property_unlock(sa);
669
0
        numalgs = sk_ALGORITHM_num(tmpalgs);
670
0
        for (i = 0; i < numalgs; i++) {
671
0
            alg = sk_ALGORITHM_value(tmpalgs, i);
672
0
            numimps = sk_IMPLEMENTATION_num(alg->impls);
673
0
            for (j = 0; j < numimps; j++)
674
0
                alg_do_one(alg, sk_IMPLEMENTATION_value(alg->impls, j), fn, fnarg);
675
0
        }
676
0
        sk_ALGORITHM_pop_free(tmpalgs, del_tmpalg);
677
0
    }
678
0
}
679
680
/**
681
 * @brief Fetches a method from the method store matching the given properties.
682
 *
683
 * This function searches the method store for an implementation of a specified
684
 * method, identified by its id (nid), and matching the given property query. If
685
 * successful, it returns the method and its associated provider.
686
 *
687
 * @param store Pointer to the OSSL_METHOD_STORE from which to fetch the method.
688
 *              Must be non-null.
689
 * @param nid (identifier) of the method to be fetched. Must be > 0
690
 * @param prop_query String containing the property query to match against.
691
 * @param prov_rw Pointer to the OSSL_PROVIDER to restrict the search to, or
692
 *                to receive the matched provider.
693
 * @param method Pointer to receive the fetched method. Must be non-null.
694
 *
695
 * @return 1 if the method is successfully fetched, 0 on failure.
696
 *
697
 * If tracing is enabled, a message is printed indicating the property query and
698
 * the resolved provider.
699
 *
700
 * NOTE: The nid parameter here is _not_ a NID in the sense of the NID_* macros.
701
 * It is a unique internal identifier value.
702
 */
703
int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
704
    int nid, const char *prop_query,
705
    const OSSL_PROVIDER **prov_rw, void **method)
706
243
{
707
243
    OSSL_PROPERTY_LIST **plp;
708
243
    ALGORITHM *alg;
709
243
    IMPLEMENTATION *impl, *best_impl = NULL;
710
243
    OSSL_PROPERTY_LIST *pq = NULL, *p2 = NULL;
711
243
    const OSSL_PROVIDER *prov = prov_rw != NULL ? *prov_rw : NULL;
712
243
    int ret = 0;
713
243
    int j, best = -1, score, optional;
714
243
    STORED_ALGORITHMS *sa;
715
716
243
    if (nid <= 0 || method == NULL || store == NULL)
717
0
        return 0;
718
719
243
#if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_AUTOLOAD_CONFIG)
720
243
    if (ossl_lib_ctx_is_default(store->ctx)
721
0
        && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
722
0
        return 0;
723
243
#endif
724
725
243
    sa = stored_algs_shard(store, nid);
726
727
    /* This only needs to be a read lock, because the query won't create anything */
728
243
    if (!ossl_property_read_lock(sa))
729
0
        return 0;
730
731
243
    OSSL_TRACE2(QUERY, "Retrieving by nid %d from stored algorithms %p\n",
732
243
        nid, (void *)sa);
733
243
    alg = ossl_method_store_retrieve(sa, nid);
734
243
    if (alg == NULL) {
735
6
        ossl_property_unlock(sa);
736
6
        OSSL_TRACE2(QUERY, "Failed to retrieve by nid %d from stored algorithms %p\n",
737
6
            nid, (void *)sa);
738
6
        return 0;
739
6
    }
740
237
    OSSL_TRACE2(QUERY, "Retrieved by nid %d from stored algorithms %p\n",
741
237
        nid, (void *)sa);
742
743
    /*
744
     * If a property query string is provided, convert it to an
745
     * OSSL_PROPERTY_LIST structure
746
     */
747
237
    if (prop_query != NULL)
748
237
        p2 = pq = ossl_parse_query(store->ctx, prop_query, 0);
749
750
    /*
751
     * If the library context has default properties specified
752
     * then merge those with the properties passed to this function
753
     */
754
237
    plp = ossl_ctx_global_properties(store->ctx, 0);
755
237
    if (plp != NULL && *plp != NULL) {
756
0
        if (pq == NULL) {
757
0
            pq = *plp;
758
0
        } else {
759
0
            p2 = ossl_property_merge(pq, *plp);
760
0
            ossl_property_free(pq);
761
0
            if (p2 == NULL)
762
0
                goto fin;
763
0
            pq = p2;
764
0
        }
765
0
    }
766
767
    /*
768
     * Search for a provider that provides this implementation.
769
     * If the requested provider is NULL, then any provider will do,
770
     * otherwise we should try to find the one that matches the requested
771
     * provider.  Note that providers are given implicit preference via the
772
     * ordering of the implementation stack
773
     */
774
237
    if (pq == NULL) {
775
0
        for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
776
0
            impl = sk_IMPLEMENTATION_value(alg->impls, j);
777
0
            if (impl != NULL
778
0
                && (prov == NULL || impl->provider == prov)) {
779
0
                best_impl = impl;
780
0
                ret = 1;
781
0
                break;
782
0
            }
783
0
        }
784
0
        goto fin;
785
0
    }
786
787
    /*
788
     * If there are optional properties specified
789
     * then run the search again, and select the provider that matches the
790
     * most options
791
     */
792
237
    optional = ossl_property_has_optional(pq);
793
237
    for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
794
237
        impl = sk_IMPLEMENTATION_value(alg->impls, j);
795
237
        if (impl != NULL
796
237
            && (prov == NULL || impl->provider == prov)) {
797
237
            score = ossl_property_match_count(pq, impl->properties);
798
237
            if (score > best) {
799
237
                best_impl = impl;
800
237
                best = score;
801
237
                ret = 1;
802
237
                if (!optional)
803
237
                    goto fin;
804
237
            }
805
237
        }
806
237
    }
807
237
fin:
808
237
    if (ret && ossl_method_up_ref(&best_impl->method)) {
809
237
        *method = best_impl->method.method;
810
237
        if (prov_rw != NULL)
811
237
            *prov_rw = best_impl->provider;
812
237
    } else {
813
0
        ret = 0;
814
0
    }
815
816
237
#ifndef FIPS_MODULE
817
237
    OSSL_TRACE_BEGIN(QUERY)
818
0
    {
819
0
        char buf[512];
820
0
        size_t size;
821
822
0
        size = ossl_property_list_to_string(NULL, pq, buf, 512);
823
0
        BIO_printf(trc_out, "method store query with properties %s "
824
0
                            "resolves to provider %s\n",
825
0
            size == 0 ? "none" : buf,
826
0
            best_impl == NULL ? "none" : ossl_provider_name(best_impl->provider));
827
0
    }
828
237
    OSSL_TRACE_END(QUERY);
829
237
#endif
830
831
237
    ossl_property_unlock(sa);
832
237
    ossl_property_free(p2);
833
237
    return ret;
834
237
}
835
836
static void ossl_method_cache_flush_alg(STORED_ALGORITHMS *sa,
837
    ALGORITHM *alg)
838
1
{
839
1
    sa->cache_nelem -= lh_QUERY_num_items(alg->cache);
840
1
    impl_cache_flush_alg(0, alg);
841
1
}
842
843
static void ossl_method_cache_flush(STORED_ALGORITHMS *sa, int nid)
844
31
{
845
31
    ALGORITHM *alg = ossl_method_store_retrieve(sa, nid);
846
847
31
    if (alg != NULL)
848
1
        ossl_method_cache_flush_alg(sa, alg);
849
31
}
850
851
int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)
852
17
{
853
85
    for (int i = 0; i < NUM_SHARDS; ++i) {
854
68
        STORED_ALGORITHMS *sa = &store->algs[i];
855
856
68
        if (!ossl_property_write_lock(sa))
857
0
            return 0;
858
68
        ossl_sa_ALGORITHM_doall(sa->algs, &impl_cache_flush_alg);
859
68
        sa->cache_nelem = 0;
860
68
        ossl_property_unlock(sa);
861
68
    }
862
863
17
    return 1;
864
17
}
865
866
IMPLEMENT_LHASH_DOALL_ARG(QUERY, IMPL_CACHE_FLUSH);
867
868
/*
869
 * Flush an element from the query cache (perhaps).
870
 *
871
 * In order to avoid taking a write lock or using atomic operations
872
 * to keep accurate least recently used (LRU) or least frequently used
873
 * (LFU) information, the procedure used here is to stochastically
874
 * flush approximately half the cache.
875
 *
876
 * This procedure isn't ideal, LRU or LFU would be better.  However,
877
 * in normal operation, reaching a full cache would be unexpected.
878
 * It means that no steady state of algorithm queries has been reached.
879
 * That is, it is most likely an attack of some form.  A suboptimal clearance
880
 * strategy that doesn't degrade performance of the normal case is
881
 * preferable to a more refined approach that imposes a performance
882
 * impact.
883
 */
884
static void impl_cache_flush_cache(QUERY *c, IMPL_CACHE_FLUSH *state)
885
0
{
886
0
    uint32_t n;
887
888
    /*
889
     * Implement the 32 bit xorshift as suggested by George Marsaglia in:
890
     *      https://doi.org/10.18637/jss.v008.i14
891
     *
892
     * This is a very fast PRNG so there is no need to extract bits one at a
893
     * time and use the entire value each time.
894
     */
895
0
    n = state->seed;
896
0
    n ^= n << 13;
897
0
    n ^= n >> 17;
898
0
    n ^= n << 5;
899
0
    state->seed = n;
900
901
0
    if ((n & 1) != 0)
902
0
        impl_cache_free(lh_QUERY_delete(state->cache, c));
903
0
    else
904
0
        state->nelem++;
905
0
}
906
907
static void impl_cache_flush_one_alg(ossl_uintmax_t idx, ALGORITHM *alg,
908
    void *v)
909
0
{
910
0
    IMPL_CACHE_FLUSH *state = (IMPL_CACHE_FLUSH *)v;
911
0
    unsigned long orig_down_load = lh_QUERY_get_down_load(alg->cache);
912
913
0
    state->cache = alg->cache;
914
0
    lh_QUERY_set_down_load(alg->cache, 0);
915
0
    lh_QUERY_doall_IMPL_CACHE_FLUSH(state->cache, &impl_cache_flush_cache,
916
0
        state);
917
0
    lh_QUERY_set_down_load(alg->cache, orig_down_load);
918
0
}
919
920
static void ossl_method_cache_flush_some(STORED_ALGORITHMS *sa)
921
0
{
922
0
    IMPL_CACHE_FLUSH state;
923
0
    static TSAN_QUALIFIER uint32_t global_seed = 1;
924
925
0
    state.nelem = 0;
926
0
    state.using_global_seed = 0;
927
0
    if ((state.seed = OPENSSL_rdtsc()) == 0) {
928
        /* If there is no timer available, seed another way */
929
0
        state.using_global_seed = 1;
930
0
        state.seed = tsan_load(&global_seed);
931
0
    }
932
933
0
    sa->cache_need_flush = 0;
934
0
    ossl_sa_ALGORITHM_doall_arg(sa->algs, &impl_cache_flush_one_alg, &state);
935
0
    sa->cache_nelem = state.nelem;
936
937
    /* Without a timer, update the global seed */
938
0
    if (state.using_global_seed)
939
0
        tsan_add(&global_seed, state.seed);
940
0
}
941
942
int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
943
    int nid, const char *prop_query, void **method)
944
786k
{
945
786k
    ALGORITHM *alg;
946
786k
    QUERY elem, *r;
947
786k
    int res = 0;
948
786k
    STORED_ALGORITHMS *sa;
949
950
786k
    if (nid <= 0 || store == NULL || prop_query == NULL)
951
0
        return 0;
952
953
786k
    sa = stored_algs_shard(store, nid);
954
786k
    if (!ossl_property_read_lock(sa))
955
0
        return 0;
956
786k
    alg = ossl_method_store_retrieve(sa, nid);
957
786k
    if (alg == NULL)
958
5
        goto err;
959
960
786k
    elem.query = prop_query;
961
786k
    elem.provider = prov;
962
786k
    r = lh_QUERY_retrieve(alg->cache, &elem);
963
786k
    if (r == NULL)
964
23
        goto err;
965
786k
    if (ossl_method_up_ref(&r->method)) {
966
786k
        *method = r->method.method;
967
786k
        res = 1;
968
786k
    }
969
786k
err:
970
786k
    ossl_property_unlock(sa);
971
786k
    return res;
972
786k
}
973
974
int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
975
    int nid, const char *prop_query, void *method,
976
    int (*method_up_ref)(void *),
977
    void (*method_destruct)(void *))
978
23
{
979
23
    QUERY elem, *old, *p = NULL;
980
23
    ALGORITHM *alg;
981
23
    STORED_ALGORITHMS *sa;
982
23
    size_t len;
983
23
    int res = 1;
984
985
23
    if (nid <= 0 || store == NULL || prop_query == NULL)
986
0
        return 0;
987
988
23
    if (!ossl_assert(prov != NULL))
989
0
        return 0;
990
991
23
    sa = stored_algs_shard(store, nid);
992
23
    if (!ossl_property_write_lock(sa))
993
0
        return 0;
994
23
    if (sa->cache_need_flush)
995
0
        ossl_method_cache_flush_some(sa);
996
23
    alg = ossl_method_store_retrieve(sa, nid);
997
23
    if (alg == NULL)
998
0
        goto err;
999
1000
23
    if (method == NULL) {
1001
0
        elem.query = prop_query;
1002
0
        elem.provider = prov;
1003
0
        if ((old = lh_QUERY_delete(alg->cache, &elem)) != NULL) {
1004
0
            impl_cache_free(old);
1005
0
            sa->cache_nelem--;
1006
0
        }
1007
0
        goto end;
1008
0
    }
1009
23
    p = OPENSSL_malloc(sizeof(*p) + (len = strlen(prop_query)));
1010
23
    if (p != NULL) {
1011
23
        p->query = p->body;
1012
23
        p->provider = prov;
1013
23
        p->method.method = method;
1014
23
        p->method.up_ref = method_up_ref;
1015
23
        p->method.free = method_destruct;
1016
23
        if (!ossl_method_up_ref(&p->method))
1017
0
            goto err;
1018
23
        memcpy((char *)p->query, prop_query, len + 1);
1019
23
        if ((old = lh_QUERY_insert(alg->cache, p)) != NULL) {
1020
0
            impl_cache_free(old);
1021
0
            goto end;
1022
0
        }
1023
23
        if (!lh_QUERY_error(alg->cache)) {
1024
23
            if (++sa->cache_nelem >= IMPL_CACHE_FLUSH_THRESHOLD)
1025
0
                sa->cache_need_flush = 1;
1026
23
            goto end;
1027
23
        }
1028
0
        ossl_method_free(&p->method);
1029
0
    }
1030
0
err:
1031
0
    res = 0;
1032
0
    OPENSSL_free(p);
1033
23
end:
1034
23
    ossl_property_unlock(sa);
1035
23
    return res;
1036
0
}