/src/openssl30/crypto/property/property.c
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | /* | 
| 2 |  |  * Copyright 2019-2023 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/core.h" | 
| 16 |  | #include "internal/property.h" | 
| 17 |  | #include "internal/provider.h" | 
| 18 |  | #include "internal/tsan_assist.h" | 
| 19 |  | #include "crypto/ctype.h" | 
| 20 |  | #include <openssl/lhash.h> | 
| 21 |  | #include <openssl/rand.h> | 
| 22 |  | #include "internal/thread_once.h" | 
| 23 |  | #include "crypto/lhash.h" | 
| 24 |  | #include "crypto/sparse_array.h" | 
| 25 |  | #include "property_local.h" | 
| 26 |  |  | 
| 27 |  | /* | 
| 28 |  |  * The number of elements in the query cache before we initiate a flush. | 
| 29 |  |  * If reducing this, also ensure the stochastic test in test/property_test.c | 
| 30 |  |  * isn't likely to fail. | 
| 31 |  |  */ | 
| 32 | 289 | #define IMPL_CACHE_FLUSH_THRESHOLD  500 | 
| 33 |  |  | 
| 34 |  | typedef struct { | 
| 35 |  |     void *method; | 
| 36 |  |     int (*up_ref)(void *); | 
| 37 |  |     void (*free)(void *); | 
| 38 |  | } METHOD; | 
| 39 |  |  | 
| 40 |  | typedef struct { | 
| 41 |  |     const OSSL_PROVIDER *provider; | 
| 42 |  |     OSSL_PROPERTY_LIST *properties; | 
| 43 |  |     METHOD method; | 
| 44 |  | } IMPLEMENTATION; | 
| 45 |  |  | 
| 46 |  | DEFINE_STACK_OF(IMPLEMENTATION) | 
| 47 |  |  | 
| 48 |  | typedef struct { | 
| 49 |  |     const OSSL_PROVIDER *provider; | 
| 50 |  |     const char *query; | 
| 51 |  |     METHOD method; | 
| 52 |  |     char body[1]; | 
| 53 |  | } QUERY; | 
| 54 |  |  | 
| 55 |  | DEFINE_LHASH_OF(QUERY); | 
| 56 |  |  | 
| 57 |  | typedef struct { | 
| 58 |  |     int nid; | 
| 59 |  |     STACK_OF(IMPLEMENTATION) *impls; | 
| 60 |  |     LHASH_OF(QUERY) *cache; | 
| 61 |  | } ALGORITHM; | 
| 62 |  |  | 
| 63 |  | struct ossl_method_store_st { | 
| 64 |  |     OSSL_LIB_CTX *ctx; | 
| 65 |  |     SPARSE_ARRAY_OF(ALGORITHM) *algs; | 
| 66 |  |     /* | 
| 67 |  |      * Lock to protect the |algs| array from concurrent writing, when | 
| 68 |  |      * individual implementations or queries are inserted.  This is used | 
| 69 |  |      * by the appropriate functions here. | 
| 70 |  |      */ | 
| 71 |  |     CRYPTO_RWLOCK *lock; | 
| 72 |  |     /* | 
| 73 |  |      * Lock to reserve the whole store.  This is used when fetching a set | 
| 74 |  |      * of algorithms, via these functions, found in crypto/core_fetch.c: | 
| 75 |  |      * ossl_method_construct_reserve_store() | 
| 76 |  |      * ossl_method_construct_unreserve_store() | 
| 77 |  |      */ | 
| 78 |  |     CRYPTO_RWLOCK *biglock; | 
| 79 |  |  | 
| 80 |  |     /* query cache specific values */ | 
| 81 |  |  | 
| 82 |  |     /* Count of the query cache entries for all algs */ | 
| 83 |  |     size_t cache_nelem; | 
| 84 |  |  | 
| 85 |  |     /* Flag: 1 if query cache entries for all algs need flushing */ | 
| 86 |  |     int cache_need_flush; | 
| 87 |  | }; | 
| 88 |  |  | 
| 89 |  | typedef struct { | 
| 90 |  |     LHASH_OF(QUERY) *cache; | 
| 91 |  |     size_t nelem; | 
| 92 |  |     uint32_t seed; | 
| 93 |  |     unsigned char using_global_seed; | 
| 94 |  | } IMPL_CACHE_FLUSH; | 
| 95 |  |  | 
| 96 |  | DEFINE_SPARSE_ARRAY_OF(ALGORITHM); | 
| 97 |  |  | 
| 98 |  | typedef struct ossl_global_properties_st { | 
| 99 |  |     OSSL_PROPERTY_LIST *list; | 
| 100 |  | #ifndef FIPS_MODULE | 
| 101 |  |     unsigned int no_mirrored : 1; | 
| 102 |  | #endif | 
| 103 |  | } OSSL_GLOBAL_PROPERTIES; | 
| 104 |  |  | 
| 105 |  | static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store, | 
| 106 |  |                                         ALGORITHM *alg); | 
| 107 |  | static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid); | 
| 108 |  |  | 
| 109 |  | /* Global properties are stored per library context */ | 
| 110 |  | static void ossl_ctx_global_properties_free(void *vglobp) | 
| 111 | 7 | { | 
| 112 | 7 |     OSSL_GLOBAL_PROPERTIES *globp = vglobp; | 
| 113 |  |  | 
| 114 | 7 |     if (globp != NULL) { | 
| 115 | 7 |         ossl_property_free(globp->list); | 
| 116 | 7 |         OPENSSL_free(globp); | 
| 117 | 7 |     } | 
| 118 | 7 | } | 
| 119 |  |  | 
| 120 |  | static void *ossl_ctx_global_properties_new(OSSL_LIB_CTX *ctx) | 
| 121 | 7 | { | 
| 122 | 7 |     return OPENSSL_zalloc(sizeof(OSSL_GLOBAL_PROPERTIES)); | 
| 123 | 7 | } | 
| 124 |  |  | 
| 125 |  | static const OSSL_LIB_CTX_METHOD ossl_ctx_global_properties_method = { | 
| 126 |  |     OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY, | 
| 127 |  |     ossl_ctx_global_properties_new, | 
| 128 |  |     ossl_ctx_global_properties_free, | 
| 129 |  | }; | 
| 130 |  |  | 
| 131 |  | OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *libctx, | 
| 132 |  |                                                 ossl_unused int loadconfig) | 
| 133 | 289 | { | 
| 134 | 289 |     OSSL_GLOBAL_PROPERTIES *globp; | 
| 135 |  |  | 
| 136 | 289 | #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_AUTOLOAD_CONFIG) | 
| 137 | 289 |     if (loadconfig && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL)) | 
| 138 | 0 |         return NULL; | 
| 139 | 289 | #endif | 
| 140 | 289 |     globp = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES, | 
| 141 | 289 |                                   &ossl_ctx_global_properties_method); | 
| 142 |  |  | 
| 143 | 289 |     return globp != NULL ? &globp->list : NULL; | 
| 144 | 289 | } | 
| 145 |  |  | 
| 146 |  | #ifndef FIPS_MODULE | 
| 147 |  | int ossl_global_properties_no_mirrored(OSSL_LIB_CTX *libctx) | 
| 148 | 0 | { | 
| 149 | 0 |     OSSL_GLOBAL_PROPERTIES *globp | 
| 150 | 0 |         = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES, | 
| 151 | 0 |                                 &ossl_ctx_global_properties_method); | 
| 152 |  | 
 | 
| 153 | 0 |     return globp != NULL && globp->no_mirrored ? 1 : 0; | 
| 154 | 0 | } | 
| 155 |  |  | 
| 156 |  | void ossl_global_properties_stop_mirroring(OSSL_LIB_CTX *libctx) | 
| 157 | 0 | { | 
| 158 | 0 |     OSSL_GLOBAL_PROPERTIES *globp | 
| 159 | 0 |         = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES, | 
| 160 | 0 |                                 &ossl_ctx_global_properties_method); | 
| 161 |  | 
 | 
| 162 | 0 |     if (globp != NULL) | 
| 163 | 0 |         globp->no_mirrored = 1; | 
| 164 | 0 | } | 
| 165 |  | #endif | 
| 166 |  |  | 
| 167 |  | static int ossl_method_up_ref(METHOD *method) | 
| 168 | 2.99M | { | 
| 169 | 2.99M |     return (*method->up_ref)(method->method); | 
| 170 | 2.99M | } | 
| 171 |  |  | 
| 172 |  | static void ossl_method_free(METHOD *method) | 
| 173 | 3.13k | { | 
| 174 | 3.13k |     (*method->free)(method->method); | 
| 175 | 3.13k | } | 
| 176 |  |  | 
| 177 |  | static __owur int ossl_property_read_lock(OSSL_METHOD_STORE *p) | 
| 178 | 3.23M | { | 
| 179 | 3.23M |     return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0; | 
| 180 | 3.23M | } | 
| 181 |  |  | 
| 182 |  | static __owur int ossl_property_write_lock(OSSL_METHOD_STORE *p) | 
| 183 | 3.21k | { | 
| 184 | 3.21k |     return p != NULL ? CRYPTO_THREAD_write_lock(p->lock) : 0; | 
| 185 | 3.21k | } | 
| 186 |  |  | 
| 187 |  | static int ossl_property_unlock(OSSL_METHOD_STORE *p) | 
| 188 | 3.24M | { | 
| 189 | 3.24M |     return p != 0 ? CRYPTO_THREAD_unlock(p->lock) : 0; | 
| 190 | 3.24M | } | 
| 191 |  |  | 
| 192 |  | static unsigned long query_hash(const QUERY *a) | 
| 193 | 2.99M | { | 
| 194 | 2.99M |     return OPENSSL_LH_strhash(a->query); | 
| 195 | 2.99M | } | 
| 196 |  |  | 
| 197 |  | static int query_cmp(const QUERY *a, const QUERY *b) | 
| 198 | 2.99M | { | 
| 199 | 2.99M |     int res = strcmp(a->query, b->query); | 
| 200 |  |  | 
| 201 | 2.99M |     if (res == 0 && a->provider != NULL && b->provider != NULL) | 
| 202 | 41.8k |         res = b->provider > a->provider ? 1 | 
| 203 | 41.8k |             : b->provider < a->provider ? -1 | 
| 204 | 41.8k |             : 0; | 
| 205 | 2.99M |     return res; | 
| 206 | 2.99M | } | 
| 207 |  |  | 
| 208 |  | static void impl_free(IMPLEMENTATION *impl) | 
| 209 | 2.84k | { | 
| 210 | 2.84k |     if (impl != NULL) { | 
| 211 | 2.84k |         ossl_method_free(&impl->method); | 
| 212 | 2.84k |         OPENSSL_free(impl); | 
| 213 | 2.84k |     } | 
| 214 | 2.84k | } | 
| 215 |  |  | 
| 216 |  | static void impl_cache_free(QUERY *elem) | 
| 217 | 289 | { | 
| 218 | 289 |     if (elem != NULL) { | 
| 219 | 289 |         ossl_method_free(&elem->method); | 
| 220 | 289 |         OPENSSL_free(elem); | 
| 221 | 289 |     } | 
| 222 | 289 | } | 
| 223 |  |  | 
| 224 |  | static void impl_cache_flush_alg(ossl_uintmax_t idx, ALGORITHM *alg) | 
| 225 | 906 | { | 
| 226 | 906 |     lh_QUERY_doall(alg->cache, &impl_cache_free); | 
| 227 | 906 |     lh_QUERY_flush(alg->cache); | 
| 228 | 906 | } | 
| 229 |  |  | 
| 230 |  | static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a, void *arg) | 
| 231 | 1.93k | { | 
| 232 | 1.93k |     OSSL_METHOD_STORE *store = arg; | 
| 233 |  |  | 
| 234 | 1.93k |     if (a != NULL) { | 
| 235 | 1.93k |         sk_IMPLEMENTATION_pop_free(a->impls, &impl_free); | 
| 236 | 1.93k |         lh_QUERY_doall(a->cache, &impl_cache_free); | 
| 237 | 1.93k |         lh_QUERY_free(a->cache); | 
| 238 | 1.93k |         OPENSSL_free(a); | 
| 239 | 1.93k |     } | 
| 240 | 1.93k |     if (store != NULL) | 
| 241 | 1.93k |         ossl_sa_ALGORITHM_set(store->algs, idx, NULL); | 
| 242 | 1.93k | } | 
| 243 |  |  | 
| 244 |  | /* | 
| 245 |  |  * The OSSL_LIB_CTX param here allows access to underlying property data needed | 
| 246 |  |  * for computation | 
| 247 |  |  */ | 
| 248 |  | OSSL_METHOD_STORE *ossl_method_store_new(OSSL_LIB_CTX *ctx) | 
| 249 | 191 | { | 
| 250 | 191 |     OSSL_METHOD_STORE *res; | 
| 251 |  |  | 
| 252 | 191 |     res = OPENSSL_zalloc(sizeof(*res)); | 
| 253 | 191 |     if (res != NULL) { | 
| 254 | 191 |         res->ctx = ctx; | 
| 255 | 191 |         if ((res->algs = ossl_sa_ALGORITHM_new()) == NULL | 
| 256 | 191 |             || (res->lock = CRYPTO_THREAD_lock_new()) == NULL | 
| 257 | 191 |             || (res->biglock = CRYPTO_THREAD_lock_new()) == NULL) { | 
| 258 | 0 |             ossl_method_store_free(res); | 
| 259 | 0 |             return NULL; | 
| 260 | 0 |         } | 
| 261 | 191 |     } | 
| 262 | 191 |     return res; | 
| 263 | 191 | } | 
| 264 |  |  | 
| 265 |  | void ossl_method_store_free(OSSL_METHOD_STORE *store) | 
| 266 | 143 | { | 
| 267 | 143 |     if (store != NULL) { | 
| 268 | 143 |         if (store->algs != NULL) | 
| 269 | 143 |             ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup, store); | 
| 270 | 143 |         ossl_sa_ALGORITHM_free(store->algs); | 
| 271 | 143 |         CRYPTO_THREAD_lock_free(store->lock); | 
| 272 | 143 |         CRYPTO_THREAD_lock_free(store->biglock); | 
| 273 | 143 |         OPENSSL_free(store); | 
| 274 | 143 |     } | 
| 275 | 143 | } | 
| 276 |  |  | 
| 277 |  | int ossl_method_lock_store(OSSL_METHOD_STORE *store) | 
| 278 | 2.22M | { | 
| 279 | 2.22M |     return store != NULL ? CRYPTO_THREAD_write_lock(store->biglock) : 0; | 
| 280 | 2.22M | } | 
| 281 |  |  | 
| 282 |  | int ossl_method_unlock_store(OSSL_METHOD_STORE *store) | 
| 283 | 2.22M | { | 
| 284 | 2.22M |     return store != NULL ? CRYPTO_THREAD_unlock(store->biglock) : 0; | 
| 285 | 2.22M | } | 
| 286 |  |  | 
| 287 |  | static ALGORITHM *ossl_method_store_retrieve(OSSL_METHOD_STORE *store, int nid) | 
| 288 | 3.24M | { | 
| 289 | 3.24M |     return ossl_sa_ALGORITHM_get(store->algs, nid); | 
| 290 | 3.24M | } | 
| 291 |  |  | 
| 292 |  | static int ossl_method_store_insert(OSSL_METHOD_STORE *store, ALGORITHM *alg) | 
| 293 | 1.93k | { | 
| 294 | 1.93k |     return ossl_sa_ALGORITHM_set(store->algs, alg->nid, alg); | 
| 295 | 1.93k | } | 
| 296 |  |  | 
| 297 |  | int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, | 
| 298 |  |                           int nid, const char *properties, void *method, | 
| 299 |  |                           int (*method_up_ref)(void *), | 
| 300 |  |                           void (*method_destruct)(void *)) | 
| 301 | 2.84k | { | 
| 302 | 2.84k |     ALGORITHM *alg = NULL; | 
| 303 | 2.84k |     IMPLEMENTATION *impl; | 
| 304 | 2.84k |     int ret = 0; | 
| 305 | 2.84k |     int i; | 
| 306 |  |  | 
| 307 | 2.84k |     if (nid <= 0 || method == NULL || store == NULL) | 
| 308 | 0 |         return 0; | 
| 309 | 2.84k |     if (properties == NULL) | 
| 310 | 0 |         properties = ""; | 
| 311 |  |  | 
| 312 | 2.84k |     if (!ossl_assert(prov != NULL)) | 
| 313 | 0 |         return 0; | 
| 314 |  |  | 
| 315 |  |     /* Create new entry */ | 
| 316 | 2.84k |     impl = OPENSSL_malloc(sizeof(*impl)); | 
| 317 | 2.84k |     if (impl == NULL) | 
| 318 | 0 |         return 0; | 
| 319 | 2.84k |     impl->method.method = method; | 
| 320 | 2.84k |     impl->method.up_ref = method_up_ref; | 
| 321 | 2.84k |     impl->method.free = method_destruct; | 
| 322 | 2.84k |     if (!ossl_method_up_ref(&impl->method)) { | 
| 323 | 0 |         OPENSSL_free(impl); | 
| 324 | 0 |         return 0; | 
| 325 | 0 |     } | 
| 326 | 2.84k |     impl->provider = prov; | 
| 327 |  |  | 
| 328 |  |     /* Insert into the hash table if required */ | 
| 329 | 2.84k |     if (!ossl_property_write_lock(store)) { | 
| 330 | 0 |         OPENSSL_free(impl); | 
| 331 | 0 |         return 0; | 
| 332 | 0 |     } | 
| 333 | 2.84k |     ossl_method_cache_flush(store, nid); | 
| 334 | 2.84k |     if ((impl->properties = ossl_prop_defn_get(store->ctx, properties)) == NULL) { | 
| 335 | 431 |         impl->properties = ossl_parse_property(store->ctx, properties); | 
| 336 | 431 |         if (impl->properties == NULL) | 
| 337 | 0 |             goto err; | 
| 338 | 431 |         if (!ossl_prop_defn_set(store->ctx, properties, &impl->properties)) { | 
| 339 | 0 |             ossl_property_free(impl->properties); | 
| 340 | 0 |             impl->properties = NULL; | 
| 341 | 0 |             goto err; | 
| 342 | 0 |         } | 
| 343 | 431 |     } | 
| 344 |  |  | 
| 345 | 2.84k |     alg = ossl_method_store_retrieve(store, nid); | 
| 346 | 2.84k |     if (alg == NULL) { | 
| 347 | 1.93k |         if ((alg = OPENSSL_zalloc(sizeof(*alg))) == NULL | 
| 348 | 1.93k |                 || (alg->impls = sk_IMPLEMENTATION_new_null()) == NULL | 
| 349 | 1.93k |                 || (alg->cache = lh_QUERY_new(&query_hash, &query_cmp)) == NULL) | 
| 350 | 0 |             goto err; | 
| 351 | 1.93k |         alg->nid = nid; | 
| 352 | 1.93k |         if (!ossl_method_store_insert(store, alg)) | 
| 353 | 0 |             goto err; | 
| 354 | 1.93k |     } | 
| 355 |  |  | 
| 356 |  |     /* Push onto stack if there isn't one there already */ | 
| 357 | 6.63k |     for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) { | 
| 358 | 3.78k |         const IMPLEMENTATION *tmpimpl = sk_IMPLEMENTATION_value(alg->impls, i); | 
| 359 |  |  | 
| 360 | 3.78k |         if (tmpimpl->provider == impl->provider | 
| 361 | 3.78k |             && tmpimpl->properties == impl->properties) | 
| 362 | 0 |             break; | 
| 363 | 3.78k |     } | 
| 364 | 2.84k |     if (i == sk_IMPLEMENTATION_num(alg->impls) | 
| 365 | 2.84k |         && sk_IMPLEMENTATION_push(alg->impls, impl)) | 
| 366 | 2.84k |         ret = 1; | 
| 367 | 2.84k |     ossl_property_unlock(store); | 
| 368 | 2.84k |     if (ret == 0) | 
| 369 | 0 |         impl_free(impl); | 
| 370 | 2.84k |     return ret; | 
| 371 |  |  | 
| 372 | 0 | err: | 
| 373 | 0 |     ossl_property_unlock(store); | 
| 374 | 0 |     alg_cleanup(0, alg, NULL); | 
| 375 | 0 |     impl_free(impl); | 
| 376 | 0 |     return 0; | 
| 377 | 2.84k | } | 
| 378 |  |  | 
| 379 |  | int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid, | 
| 380 |  |                              const void *method) | 
| 381 | 0 | { | 
| 382 | 0 |     ALGORITHM *alg = NULL; | 
| 383 | 0 |     int i; | 
| 384 |  | 
 | 
| 385 | 0 |     if (nid <= 0 || method == NULL || store == NULL) | 
| 386 | 0 |         return 0; | 
| 387 |  |  | 
| 388 | 0 |     if (!ossl_property_write_lock(store)) | 
| 389 | 0 |         return 0; | 
| 390 | 0 |     ossl_method_cache_flush(store, nid); | 
| 391 | 0 |     alg = ossl_method_store_retrieve(store, nid); | 
| 392 | 0 |     if (alg == NULL) { | 
| 393 | 0 |         ossl_property_unlock(store); | 
| 394 | 0 |         return 0; | 
| 395 | 0 |     } | 
| 396 |  |  | 
| 397 |  |     /* | 
| 398 |  |      * A sorting find then a delete could be faster but these stacks should be | 
| 399 |  |      * relatively small, so we avoid the overhead.  Sorting could also surprise | 
| 400 |  |      * users when result orderings change (even though they are not guaranteed). | 
| 401 |  |      */ | 
| 402 | 0 |     for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) { | 
| 403 | 0 |         IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i); | 
| 404 |  | 
 | 
| 405 | 0 |         if (impl->method.method == method) { | 
| 406 | 0 |             impl_free(impl); | 
| 407 | 0 |             (void)sk_IMPLEMENTATION_delete(alg->impls, i); | 
| 408 | 0 |             ossl_property_unlock(store); | 
| 409 | 0 |             return 1; | 
| 410 | 0 |         } | 
| 411 | 0 |     } | 
| 412 | 0 |     ossl_property_unlock(store); | 
| 413 | 0 |     return 0; | 
| 414 | 0 | } | 
| 415 |  |  | 
| 416 |  | struct alg_cleanup_by_provider_data_st { | 
| 417 |  |     OSSL_METHOD_STORE *store; | 
| 418 |  |     const OSSL_PROVIDER *prov; | 
| 419 |  | }; | 
| 420 |  |  | 
| 421 |  | static void | 
| 422 |  | alg_cleanup_by_provider(ossl_uintmax_t idx, ALGORITHM *alg, void *arg) | 
| 423 | 0 | { | 
| 424 | 0 |     struct alg_cleanup_by_provider_data_st *data = arg; | 
| 425 | 0 |     int i, count; | 
| 426 |  |  | 
| 427 |  |     /* | 
| 428 |  |      * We walk the stack backwards, to avoid having to deal with stack shifts | 
| 429 |  |      * caused by deletion | 
| 430 |  |      */ | 
| 431 | 0 |     for (count = 0, i = sk_IMPLEMENTATION_num(alg->impls); i-- > 0;) { | 
| 432 | 0 |         IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i); | 
| 433 |  | 
 | 
| 434 | 0 |         if (impl->provider == data->prov) { | 
| 435 | 0 |             impl_free(impl); | 
| 436 | 0 |             (void)sk_IMPLEMENTATION_delete(alg->impls, i); | 
| 437 | 0 |             count++; | 
| 438 | 0 |         } | 
| 439 | 0 |     } | 
| 440 |  |  | 
| 441 |  |     /* | 
| 442 |  |      * If we removed any implementation, we also clear the whole associated | 
| 443 |  |      * cache, 'cause that's the sensible thing to do. | 
| 444 |  |      * There's no point flushing the cache entries where we didn't remove | 
| 445 |  |      * any implementation, though. | 
| 446 |  |      */ | 
| 447 | 0 |     if (count > 0) | 
| 448 | 0 |         ossl_method_cache_flush_alg(data->store, alg); | 
| 449 | 0 | } | 
| 450 |  |  | 
| 451 |  | int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, | 
| 452 |  |                                           const OSSL_PROVIDER *prov) | 
| 453 | 0 | { | 
| 454 | 0 |     struct alg_cleanup_by_provider_data_st data; | 
| 455 |  | 
 | 
| 456 | 0 |     if (!ossl_property_write_lock(store)) | 
| 457 | 0 |         return 0; | 
| 458 | 0 |     data.prov = prov; | 
| 459 | 0 |     data.store = store; | 
| 460 | 0 |     ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup_by_provider, &data); | 
| 461 | 0 |     ossl_property_unlock(store); | 
| 462 | 0 |     return 1; | 
| 463 | 0 | } | 
| 464 |  |  | 
| 465 |  | static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl, | 
| 466 |  |                        void (*fn)(int id, void *method, void *fnarg), | 
| 467 |  |                        void *fnarg) | 
| 468 | 49.0M | { | 
| 469 | 49.0M |     fn(alg->nid, impl->method.method, fnarg); | 
| 470 | 49.0M | } | 
| 471 |  |  | 
| 472 |  | struct alg_do_each_data_st { | 
| 473 |  |     void (*fn)(int id, void *method, void *fnarg); | 
| 474 |  |     void *fnarg; | 
| 475 |  | }; | 
| 476 |  |  | 
| 477 |  | static void alg_do_each(ossl_uintmax_t idx, ALGORITHM *alg, void *arg) | 
| 478 | 31.2M | { | 
| 479 | 31.2M |     struct alg_do_each_data_st *data = arg; | 
| 480 | 31.2M |     int i, end = sk_IMPLEMENTATION_num(alg->impls); | 
| 481 |  |  | 
| 482 | 80.3M |     for (i = 0; i < end; i++) { | 
| 483 | 49.0M |         IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i); | 
| 484 |  |  | 
| 485 | 49.0M |         alg_do_one(alg, impl, data->fn, data->fnarg); | 
| 486 | 49.0M |     } | 
| 487 | 31.2M | } | 
| 488 |  |  | 
| 489 |  | void ossl_method_store_do_all(OSSL_METHOD_STORE *store, | 
| 490 |  |                               void (*fn)(int id, void *method, void *fnarg), | 
| 491 |  |                               void *fnarg) | 
| 492 | 807k | { | 
| 493 | 807k |     struct alg_do_each_data_st data; | 
| 494 |  |  | 
| 495 | 807k |     data.fn = fn; | 
| 496 | 807k |     data.fnarg = fnarg; | 
| 497 | 807k |     if (store != NULL) | 
| 498 | 807k |         ossl_sa_ALGORITHM_doall_arg(store->algs, alg_do_each, &data); | 
| 499 | 807k | } | 
| 500 |  |  | 
| 501 |  | int ossl_method_store_fetch(OSSL_METHOD_STORE *store, | 
| 502 |  |                             int nid, const char *prop_query, | 
| 503 |  |                             const OSSL_PROVIDER **prov_rw, void **method) | 
| 504 | 122k | { | 
| 505 | 122k |     OSSL_PROPERTY_LIST **plp; | 
| 506 | 122k |     ALGORITHM *alg; | 
| 507 | 122k |     IMPLEMENTATION *impl, *best_impl = NULL; | 
| 508 | 122k |     OSSL_PROPERTY_LIST *pq = NULL, *p2 = NULL; | 
| 509 | 122k |     const OSSL_PROVIDER *prov = prov_rw != NULL ? *prov_rw : NULL; | 
| 510 | 122k |     int ret = 0; | 
| 511 | 122k |     int j, best = -1, score, optional; | 
| 512 |  |  | 
| 513 | 122k |     if (nid <= 0 || method == NULL || store == NULL) | 
| 514 | 0 |         return 0; | 
| 515 |  |  | 
| 516 | 122k | #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_AUTOLOAD_CONFIG) | 
| 517 | 122k |     if (ossl_lib_ctx_is_default(store->ctx) | 
| 518 | 122k |             && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL)) | 
| 519 | 0 |         return 0; | 
| 520 | 122k | #endif | 
| 521 |  |  | 
| 522 |  |     /* This only needs to be a read lock, because the query won't create anything */ | 
| 523 | 122k |     if (!ossl_property_read_lock(store)) | 
| 524 | 0 |         return 0; | 
| 525 | 122k |     alg = ossl_method_store_retrieve(store, nid); | 
| 526 | 122k |     if (alg == NULL) { | 
| 527 | 121k |         ossl_property_unlock(store); | 
| 528 | 121k |         return 0; | 
| 529 | 121k |     } | 
| 530 |  |  | 
| 531 | 289 |     if (prop_query != NULL) | 
| 532 | 289 |         p2 = pq = ossl_parse_query(store->ctx, prop_query, 0); | 
| 533 | 289 |     plp = ossl_ctx_global_properties(store->ctx, 0); | 
| 534 | 289 |     if (plp != NULL && *plp != NULL) { | 
| 535 | 0 |         if (pq == NULL) { | 
| 536 | 0 |             pq = *plp; | 
| 537 | 0 |         } else { | 
| 538 | 0 |             p2 = ossl_property_merge(pq, *plp); | 
| 539 | 0 |             ossl_property_free(pq); | 
| 540 | 0 |             if (p2 == NULL) | 
| 541 | 0 |                 goto fin; | 
| 542 | 0 |             pq = p2; | 
| 543 | 0 |         } | 
| 544 | 0 |     } | 
| 545 |  |  | 
| 546 | 289 |     if (pq == NULL) { | 
| 547 | 0 |         for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) { | 
| 548 | 0 |             if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL | 
| 549 | 0 |                 && (prov == NULL || impl->provider == prov)) { | 
| 550 | 0 |                 best_impl = impl; | 
| 551 | 0 |                 ret = 1; | 
| 552 | 0 |                 break; | 
| 553 | 0 |             } | 
| 554 | 0 |         } | 
| 555 | 0 |         goto fin; | 
| 556 | 0 |     } | 
| 557 | 289 |     optional = ossl_property_has_optional(pq); | 
| 558 | 289 |     for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) { | 
| 559 | 289 |         if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL | 
| 560 | 289 |             && (prov == NULL || impl->provider == prov)) { | 
| 561 | 289 |             score = ossl_property_match_count(pq, impl->properties); | 
| 562 | 289 |             if (score > best) { | 
| 563 | 289 |                 best_impl = impl; | 
| 564 | 289 |                 best = score; | 
| 565 | 289 |                 ret = 1; | 
| 566 | 289 |                 if (!optional) | 
| 567 | 289 |                     goto fin; | 
| 568 | 289 |             } | 
| 569 | 289 |         } | 
| 570 | 289 |     } | 
| 571 | 289 | fin: | 
| 572 | 289 |     if (ret && ossl_method_up_ref(&best_impl->method)) { | 
| 573 | 289 |         *method = best_impl->method.method; | 
| 574 | 289 |         if (prov_rw != NULL) | 
| 575 | 289 |             *prov_rw = best_impl->provider; | 
| 576 | 289 |     } else { | 
| 577 | 0 |         ret = 0; | 
| 578 | 0 |     } | 
| 579 | 289 |     ossl_property_unlock(store); | 
| 580 | 289 |     ossl_property_free(p2); | 
| 581 | 289 |     return ret; | 
| 582 | 289 | } | 
| 583 |  |  | 
| 584 |  | static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store, | 
| 585 |  |                                         ALGORITHM *alg) | 
| 586 | 906 | { | 
| 587 | 906 |     store->cache_nelem -= lh_QUERY_num_items(alg->cache); | 
| 588 | 906 |     impl_cache_flush_alg(0, alg); | 
| 589 | 906 | } | 
| 590 |  |  | 
| 591 |  | static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid) | 
| 592 | 2.84k | { | 
| 593 | 2.84k |     ALGORITHM *alg = ossl_method_store_retrieve(store, nid); | 
| 594 |  |  | 
| 595 | 2.84k |     if (alg != NULL) | 
| 596 | 906 |         ossl_method_cache_flush_alg(store, alg); | 
| 597 | 2.84k | } | 
| 598 |  |  | 
| 599 |  | int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store) | 
| 600 | 88 | { | 
| 601 | 88 |     if (!ossl_property_write_lock(store)) | 
| 602 | 0 |         return 0; | 
| 603 | 88 |     ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg); | 
| 604 | 88 |     store->cache_nelem = 0; | 
| 605 | 88 |     ossl_property_unlock(store); | 
| 606 | 88 |     return 1; | 
| 607 | 88 | } | 
| 608 |  |  | 
| 609 |  | IMPLEMENT_LHASH_DOALL_ARG(QUERY, IMPL_CACHE_FLUSH); | 
| 610 |  |  | 
| 611 |  | /* | 
| 612 |  |  * Flush an element from the query cache (perhaps). | 
| 613 |  |  * | 
| 614 |  |  * In order to avoid taking a write lock or using atomic operations | 
| 615 |  |  * to keep accurate least recently used (LRU) or least frequently used | 
| 616 |  |  * (LFU) information, the procedure used here is to stochastically | 
| 617 |  |  * flush approximately half the cache. | 
| 618 |  |  * | 
| 619 |  |  * This procedure isn't ideal, LRU or LFU would be better.  However, | 
| 620 |  |  * in normal operation, reaching a full cache would be unexpected. | 
| 621 |  |  * It means that no steady state of algorithm queries has been reached. | 
| 622 |  |  * That is, it is most likely an attack of some form.  A suboptimal clearance | 
| 623 |  |  * strategy that doesn't degrade performance of the normal case is | 
| 624 |  |  * preferable to a more refined approach that imposes a performance | 
| 625 |  |  * impact. | 
| 626 |  |  */ | 
| 627 |  | static void impl_cache_flush_cache(QUERY *c, IMPL_CACHE_FLUSH *state) | 
| 628 | 0 | { | 
| 629 | 0 |     uint32_t n; | 
| 630 |  |  | 
| 631 |  |     /* | 
| 632 |  |      * Implement the 32 bit xorshift as suggested by George Marsaglia in: | 
| 633 |  |      *      https://doi.org/10.18637/jss.v008.i14 | 
| 634 |  |      * | 
| 635 |  |      * This is a very fast PRNG so there is no need to extract bits one at a | 
| 636 |  |      * time and use the entire value each time. | 
| 637 |  |      */ | 
| 638 | 0 |     n = state->seed; | 
| 639 | 0 |     n ^= n << 13; | 
| 640 | 0 |     n ^= n >> 17; | 
| 641 | 0 |     n ^= n << 5; | 
| 642 | 0 |     state->seed = n; | 
| 643 |  | 
 | 
| 644 | 0 |     if ((n & 1) != 0) | 
| 645 | 0 |         impl_cache_free(lh_QUERY_delete(state->cache, c)); | 
| 646 | 0 |     else | 
| 647 | 0 |         state->nelem++; | 
| 648 | 0 | } | 
| 649 |  |  | 
| 650 |  | static void impl_cache_flush_one_alg(ossl_uintmax_t idx, ALGORITHM *alg, | 
| 651 |  |                                      void *v) | 
| 652 | 0 | { | 
| 653 | 0 |     IMPL_CACHE_FLUSH *state = (IMPL_CACHE_FLUSH *)v; | 
| 654 |  | 
 | 
| 655 | 0 |     state->cache = alg->cache; | 
| 656 | 0 |     lh_QUERY_doall_IMPL_CACHE_FLUSH(state->cache, &impl_cache_flush_cache, | 
| 657 | 0 |                                     state); | 
| 658 | 0 | } | 
| 659 |  |  | 
| 660 |  | static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store) | 
| 661 | 0 | { | 
| 662 | 0 |     IMPL_CACHE_FLUSH state; | 
| 663 | 0 |     static TSAN_QUALIFIER uint32_t global_seed = 1; | 
| 664 |  | 
 | 
| 665 | 0 |     state.nelem = 0; | 
| 666 | 0 |     state.using_global_seed = 0; | 
| 667 | 0 |     if ((state.seed = OPENSSL_rdtsc()) == 0) { | 
| 668 |  |         /* If there is no timer available, seed another way */ | 
| 669 | 0 |         state.using_global_seed = 1; | 
| 670 | 0 |         state.seed = tsan_load(&global_seed); | 
| 671 | 0 |     } | 
| 672 | 0 |     store->cache_need_flush = 0; | 
| 673 | 0 |     ossl_sa_ALGORITHM_doall_arg(store->algs, &impl_cache_flush_one_alg, &state); | 
| 674 | 0 |     store->cache_nelem = state.nelem; | 
| 675 |  |     /* Without a timer, update the global seed */ | 
| 676 | 0 |     if (state.using_global_seed) | 
| 677 | 0 |         tsan_store(&global_seed, state.seed); | 
| 678 | 0 | } | 
| 679 |  |  | 
| 680 |  | int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, | 
| 681 |  |                                 int nid, const char *prop_query, void **method) | 
| 682 | 3.11M | { | 
| 683 | 3.11M |     ALGORITHM *alg; | 
| 684 | 3.11M |     QUERY elem, *r; | 
| 685 | 3.11M |     int res = 0; | 
| 686 |  |  | 
| 687 | 3.11M |     if (nid <= 0 || store == NULL || prop_query == NULL) | 
| 688 | 0 |         return 0; | 
| 689 |  |  | 
| 690 | 3.11M |     if (!ossl_property_read_lock(store)) | 
| 691 | 0 |         return 0; | 
| 692 | 3.11M |     alg = ossl_method_store_retrieve(store, nid); | 
| 693 | 3.11M |     if (alg == NULL) | 
| 694 | 121k |         goto err; | 
| 695 |  |  | 
| 696 | 2.99M |     elem.query = prop_query; | 
| 697 | 2.99M |     elem.provider = prov; | 
| 698 | 2.99M |     r = lh_QUERY_retrieve(alg->cache, &elem); | 
| 699 | 2.99M |     if (r == NULL) | 
| 700 | 228 |         goto err; | 
| 701 | 2.99M |     if (ossl_method_up_ref(&r->method)) { | 
| 702 | 2.99M |         *method = r->method.method; | 
| 703 | 2.99M |         res = 1; | 
| 704 | 2.99M |     } | 
| 705 | 3.11M | err: | 
| 706 | 3.11M |     ossl_property_unlock(store); | 
| 707 | 3.11M |     return res; | 
| 708 | 2.99M | } | 
| 709 |  |  | 
| 710 |  | int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, | 
| 711 |  |                                 int nid, const char *prop_query, void *method, | 
| 712 |  |                                 int (*method_up_ref)(void *), | 
| 713 |  |                                 void (*method_destruct)(void *)) | 
| 714 | 289 | { | 
| 715 | 289 |     QUERY elem, *old, *p = NULL; | 
| 716 | 289 |     ALGORITHM *alg; | 
| 717 | 289 |     size_t len; | 
| 718 | 289 |     int res = 1; | 
| 719 |  |  | 
| 720 | 289 |     if (nid <= 0 || store == NULL || prop_query == NULL) | 
| 721 | 0 |         return 0; | 
| 722 |  |  | 
| 723 | 289 |     if (!ossl_assert(prov != NULL)) | 
| 724 | 0 |         return 0; | 
| 725 |  |  | 
| 726 | 289 |     if (!ossl_property_write_lock(store)) | 
| 727 | 0 |         return 0; | 
| 728 | 289 |     if (store->cache_need_flush) | 
| 729 | 0 |         ossl_method_cache_flush_some(store); | 
| 730 | 289 |     alg = ossl_method_store_retrieve(store, nid); | 
| 731 | 289 |     if (alg == NULL) | 
| 732 | 0 |         goto err; | 
| 733 |  |  | 
| 734 | 289 |     if (method == NULL) { | 
| 735 | 0 |         elem.query = prop_query; | 
| 736 | 0 |         elem.provider = prov; | 
| 737 | 0 |         if ((old = lh_QUERY_delete(alg->cache, &elem)) != NULL) { | 
| 738 | 0 |             impl_cache_free(old); | 
| 739 | 0 |             store->cache_nelem--; | 
| 740 | 0 |         } | 
| 741 | 0 |         goto end; | 
| 742 | 0 |     } | 
| 743 | 289 |     p = OPENSSL_malloc(sizeof(*p) + (len = strlen(prop_query))); | 
| 744 | 289 |     if (p != NULL) { | 
| 745 | 289 |         p->query = p->body; | 
| 746 | 289 |         p->provider = prov; | 
| 747 | 289 |         p->method.method = method; | 
| 748 | 289 |         p->method.up_ref = method_up_ref; | 
| 749 | 289 |         p->method.free = method_destruct; | 
| 750 | 289 |         if (!ossl_method_up_ref(&p->method)) | 
| 751 | 0 |             goto err; | 
| 752 | 289 |         memcpy((char *)p->query, prop_query, len + 1); | 
| 753 | 289 |         if ((old = lh_QUERY_insert(alg->cache, p)) != NULL) { | 
| 754 | 0 |             impl_cache_free(old); | 
| 755 | 0 |             goto end; | 
| 756 | 0 |         } | 
| 757 | 289 |         if (!lh_QUERY_error(alg->cache)) { | 
| 758 | 289 |             if (++store->cache_nelem >= IMPL_CACHE_FLUSH_THRESHOLD) | 
| 759 | 0 |                 store->cache_need_flush = 1; | 
| 760 | 289 |             goto end; | 
| 761 | 289 |         } | 
| 762 | 0 |         ossl_method_free(&p->method); | 
| 763 | 0 |     } | 
| 764 | 0 | err: | 
| 765 | 0 |     res = 0; | 
| 766 | 0 |     OPENSSL_free(p); | 
| 767 | 289 | end: | 
| 768 | 289 |     ossl_property_unlock(store); | 
| 769 | 289 |     return res; | 
| 770 | 0 | } |