Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/threads_pthread.c
Line
Count
Source
1
/*
2
 * Copyright 2016-2026 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
/* We need to use the OPENSSL_fork_*() deprecated APIs */
11
#define OPENSSL_SUPPRESS_DEPRECATED
12
13
#if !defined(__GNUC__) || !defined(__ATOMIC_ACQ_REL) || defined(BROKEN_CLANG_ATOMICS) || defined(OPENSSL_NO_STDIO)
14
/*
15
 * we only enable REPORT_RWLOCK_CONTENTION on clang/gcc when we have
16
 * atomics available.  We do this because we need to use an atomic to track
17
 * when we can close the log file.  We could use the CRYPTO_atomic_ api
18
 * but that requires lock creation which gets us into a bad recursive loop
19
 * when we try to initialize the file pointer
20
 */
21
#ifdef REPORT_RWLOCK_CONTENTION
22
#warning "RWLOCK CONTENTION REPORTING NOT SUPPORTED, Disabling"
23
#undef REPORT_RWLOCK_CONTENTION
24
#endif
25
#endif
26
27
#ifdef REPORT_RWLOCK_CONTENTION
28
#define _GNU_SOURCE
29
#include <execinfo.h>
30
#include <unistd.h>
31
#include <limits.h>
32
#include <stdlib.h>
33
#endif
34
35
#include <openssl/crypto.h>
36
#include <crypto/cryptlib.h>
37
#include <crypto/sparse_array.h>
38
#include "internal/cryptlib.h"
39
#include "internal/threads_common.h"
40
#include "internal/rcu.h"
41
#ifdef REPORT_RWLOCK_CONTENTION
42
#include <fcntl.h>
43
#include <stdbool.h>
44
#include <sys/syscall.h>
45
#include <sys/uio.h>
46
#include "internal/time.h"
47
#endif
48
#include "rcu_internal.h"
49
50
#if defined(__SANITIZE_THREAD__)
51
#define TSAN_FAKE_UNLOCK(x)          \
52
    __tsan_mutex_pre_unlock((x), 0); \
53
    __tsan_mutex_post_unlock((x), 0)
54
55
#define TSAN_FAKE_LOCK(x)          \
56
    __tsan_mutex_pre_lock((x), 0); \
57
    __tsan_mutex_post_lock((x), 0, 0)
58
59
#define TSAN_LOAD_MEM_ORDER __ATOMIC_ACQUIRE
60
#define TSAN_STORE_MEM_ORDER __ATOMIC_RELEASE
61
#else
62
#define TSAN_FAKE_UNLOCK(x)
63
#define TSAN_FAKE_LOCK(x)
64
27.0M
#define TSAN_LOAD_MEM_ORDER __ATOMIC_RELAXED
65
100
#define TSAN_STORE_MEM_ORDER __ATOMIC_RELAXED
66
#endif
67
68
#if defined(__sun)
69
#include <atomic.h>
70
#endif
71
72
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS)
73
74
#if defined(OPENSSL_SYS_UNIX)
75
#include <sys/types.h>
76
#include <unistd.h>
77
#endif
78
79
#include <assert.h>
80
81
/*
82
 * The Non-Stop KLT thread model currently seems broken in its rwlock
83
 * implementation
84
 * Likewise is there a problem with the glibc implementation on riscv.
85
 */
86
#if defined(PTHREAD_RWLOCK_INITIALIZER) && !defined(_KLT_MODEL_) && !defined(_PUT_MODEL_) \
87
    && !defined(__riscv)
88
#define USE_RWLOCK
89
#endif
90
91
/*
92
 * For all GNU/clang atomic builtins, we also need fallbacks, to cover all
93
 * other compilers.
94
95
 * Unfortunately, we can't do that with some "generic type", because there's no
96
 * guarantee that the chosen generic type is large enough to cover all cases.
97
 * Therefore, we implement fallbacks for each applicable type, with composed
98
 * names that include the type they handle.
99
 *
100
 * (an anecdote: we previously tried to use |void *| as the generic type, with
101
 * the thought that the pointer itself is the largest type.  However, this is
102
 * not true on 32-bit pointer platforms, as a |uint64_t| is twice as large)
103
 *
104
 * All applicable ATOMIC_ macros take the intended type as first parameter, so
105
 * they can map to the correct fallback function.  In the GNU/clang case, that
106
 * parameter is simply ignored.
107
 */
108
109
/*
110
 * Internal types used with the ATOMIC_ macros, to make it possible to compose
111
 * fallback function names.
112
 */
113
typedef void *pvoid;
114
115
#if defined(OSSL_USE_GCC_ATOMICS)
116
2.77M
#define ATOMIC_LOAD_N(t, p, o) __atomic_load_n(p, o)
117
0
#define ATOMIC_STORE_N(t, p, v, o) __atomic_store_n(p, v, o)
118
1.02k
#define ATOMIC_STORE(t, p, v, o) __atomic_store(p, v, o)
119
0
#define ATOMIC_ADD_FETCH(p, v, o) __atomic_add_fetch(p, v, o)
120
0
#define ATOMIC_SUB_FETCH(p, v, o) __atomic_sub_fetch(p, v, o)
121
#else
122
static pthread_mutex_t atomic_sim_lock = PTHREAD_MUTEX_INITIALIZER;
123
124
#define IMPL_fallback_atomic_load_n(t)                    \
125
    static ossl_inline t fallback_atomic_load_n_##t(t *p) \
126
    {                                                     \
127
        t ret;                                            \
128
                                                          \
129
        pthread_mutex_lock(&atomic_sim_lock);             \
130
        ret = *p;                                         \
131
        pthread_mutex_unlock(&atomic_sim_lock);           \
132
        return ret;                                       \
133
    }
134
IMPL_fallback_atomic_load_n(uint32_t)
135
    IMPL_fallback_atomic_load_n(uint64_t)
136
        IMPL_fallback_atomic_load_n(pvoid)
137
138
#define ATOMIC_LOAD_N(t, p, o) fallback_atomic_load_n_##t(p)
139
140
#define IMPL_fallback_atomic_store_n(t)                         \
141
    static ossl_inline t fallback_atomic_store_n_##t(t *p, t v) \
142
    {                                                           \
143
        t ret;                                                  \
144
                                                                \
145
        pthread_mutex_lock(&atomic_sim_lock);                   \
146
        ret = *p;                                               \
147
        *p = v;                                                 \
148
        pthread_mutex_unlock(&atomic_sim_lock);                 \
149
        return ret;                                             \
150
    }
151
            IMPL_fallback_atomic_store_n(uint32_t)
152
153
#define ATOMIC_STORE_N(t, p, v, o) fallback_atomic_store_n_##t(p, v)
154
155
#define IMPL_fallback_atomic_store(t)                             \
156
    static ossl_inline void fallback_atomic_store_##t(t *p, t *v) \
157
    {                                                             \
158
        pthread_mutex_lock(&atomic_sim_lock);                     \
159
        *p = *v;                                                  \
160
        pthread_mutex_unlock(&atomic_sim_lock);                   \
161
    }
162
                IMPL_fallback_atomic_store(pvoid)
163
164
#define ATOMIC_STORE(t, p, v, o) fallback_atomic_store_##t(p, v)
165
166
    /*
167
     * The fallbacks that follow don't need any per type implementation, as
168
     * they are designed for uint64_t only.  If there comes a time when multiple
169
     * types need to be covered, it's relatively easy to refactor them the same
170
     * way as the fallbacks above.
171
     */
172
173
    static ossl_inline uint64_t fallback_atomic_add_fetch(uint64_t *p, uint64_t v)
174
{
175
    uint64_t ret;
176
177
    pthread_mutex_lock(&atomic_sim_lock);
178
    *p += v;
179
    ret = *p;
180
    pthread_mutex_unlock(&atomic_sim_lock);
181
    return ret;
182
}
183
184
#define ATOMIC_ADD_FETCH(p, v, o) fallback_atomic_add_fetch(p, v)
185
186
static ossl_inline uint64_t fallback_atomic_sub_fetch(uint64_t *p, uint64_t v)
187
{
188
    uint64_t ret;
189
190
    pthread_mutex_lock(&atomic_sim_lock);
191
    *p -= v;
192
    ret = *p;
193
    pthread_mutex_unlock(&atomic_sim_lock);
194
    return ret;
195
}
196
197
#define ATOMIC_SUB_FETCH(p, v, o) fallback_atomic_sub_fetch(p, v)
198
#endif
199
200
/*
201
 * This is the core of an rcu lock. It tracks the readers and writers for the
202
 * current quiescence point for a given lock. Users is the 64 bit value that
203
 * stores the READERS/ID as defined above
204
 *
205
 */
206
struct rcu_qp {
207
    uint64_t users;
208
};
209
210
struct thread_qp {
211
    struct rcu_qp *qp;
212
    unsigned int depth;
213
    CRYPTO_RCU_LOCK *lock;
214
};
215
216
0
#define MAX_QPS 10
217
/*
218
 * This is the per thread tracking data
219
 * that is assigned to each thread participating
220
 * in an rcu qp
221
 *
222
 * qp points to the qp that it last acquired
223
 *
224
 */
225
struct rcu_thr_data {
226
    struct thread_qp thread_qps[MAX_QPS];
227
};
228
229
/*
230
 * This is the internal version of a CRYPTO_RCU_LOCK
231
 * it is cast from CRYPTO_RCU_LOCK
232
 */
233
struct rcu_lock_st {
234
    /* Callbacks to call for next ossl_synchronize_rcu */
235
    struct rcu_cb_item *cb_items;
236
237
    /* The context we are being created against */
238
    OSSL_LIB_CTX *ctx;
239
240
    /* Array of quiescent points for synchronization */
241
    struct rcu_qp *qp_group;
242
243
    /* rcu generation counter for in-order retirement */
244
    uint32_t id_ctr;
245
246
    /* Number of elements in qp_group array */
247
    uint32_t group_count;
248
249
    /* Index of the current qp in the qp_group array */
250
    uint32_t reader_idx;
251
252
    /* value of the next id_ctr value to be retired */
253
    uint32_t next_to_retire;
254
255
    /* index of the next free rcu_qp in the qp_group */
256
    uint32_t current_alloc_idx;
257
258
    /* number of qp's in qp_group array currently being retired */
259
    uint32_t writers_alloced;
260
261
    /* lock protecting write side operations */
262
    pthread_mutex_t write_lock;
263
264
    /* lock protecting updates to writers_alloced/current_alloc_idx */
265
    pthread_mutex_t alloc_lock;
266
267
    /* signal to wake threads waiting on alloc_lock */
268
    pthread_cond_t alloc_signal;
269
270
    /* lock to enforce in-order retirement */
271
    pthread_mutex_t prior_lock;
272
273
    /* signal to wake threads waiting on prior_lock */
274
    pthread_cond_t prior_signal;
275
};
276
277
/* Read side acquisition of the current qp */
278
static struct rcu_qp *get_hold_current_qp(struct rcu_lock_st *lock)
279
0
{
280
0
    uint32_t qp_idx;
281
282
    /* get the current qp index */
283
0
    for (;;) {
284
0
        qp_idx = ATOMIC_LOAD_N(uint32_t, &lock->reader_idx, __ATOMIC_RELAXED);
285
286
        /*
287
         * Notes on use of __ATOMIC_ACQUIRE
288
         * We need to ensure the following:
289
         * 1) That subsequent operations aren't optimized by hoisting them above
290
         * this operation.  Specifically, we don't want the below re-load of
291
         * qp_idx to get optimized away
292
         * 2) We want to ensure that any updating of reader_idx on the write side
293
         * of the lock is flushed from a local cpu cache so that we see any
294
         * updates prior to the load.  This is a non-issue on cache coherent
295
         * systems like x86, but is relevant on other arches
296
         */
297
0
        ATOMIC_ADD_FETCH(&lock->qp_group[qp_idx].users, (uint64_t)1,
298
0
            __ATOMIC_ACQUIRE);
299
300
        /* if the idx hasn't changed, we're good, else try again */
301
0
        if (qp_idx == ATOMIC_LOAD_N(uint32_t, &lock->reader_idx, __ATOMIC_ACQUIRE))
302
0
            break;
303
304
0
        ATOMIC_SUB_FETCH(&lock->qp_group[qp_idx].users, (uint64_t)1,
305
0
            __ATOMIC_RELAXED);
306
0
    }
307
308
0
    return &lock->qp_group[qp_idx];
309
0
}
310
311
static void ossl_rcu_free_local_data(void *arg)
312
0
{
313
0
    OSSL_LIB_CTX *ctx = arg;
314
0
    struct rcu_thr_data *data = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, ctx);
315
316
0
    CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, ctx, NULL);
317
0
    OPENSSL_free(data);
318
0
}
319
320
int ossl_rcu_read_lock(CRYPTO_RCU_LOCK *lock)
321
0
{
322
0
    struct rcu_thr_data *data;
323
0
    int i, available_qp = -1;
324
325
    /*
326
     * we're going to access current_qp here so ask the
327
     * processor to fetch it
328
     */
329
0
    data = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx);
330
331
0
    if (data == NULL) {
332
0
        data = OPENSSL_zalloc(sizeof(*data));
333
0
        if (data == NULL)
334
0
            return 0;
335
336
0
        if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx, data)) {
337
0
            OPENSSL_free(data);
338
0
            return 0;
339
0
        }
340
0
        if (!ossl_init_thread_start(NULL, lock->ctx, ossl_rcu_free_local_data)) {
341
0
            OPENSSL_free(data);
342
0
            CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx, NULL);
343
0
            return 0;
344
0
        }
345
0
    }
346
347
0
    for (i = 0; i < MAX_QPS; i++) {
348
0
        if (data->thread_qps[i].qp == NULL && available_qp == -1)
349
0
            available_qp = i;
350
        /* If we have a hold on this lock already, we're good */
351
0
        if (data->thread_qps[i].lock == lock) {
352
0
            data->thread_qps[i].depth++;
353
0
            return 1;
354
0
        }
355
0
    }
356
357
    /*
358
     * if we get here, then we don't have a hold on this lock yet
359
     */
360
0
    assert(available_qp != -1);
361
362
0
    data->thread_qps[available_qp].qp = get_hold_current_qp(lock);
363
0
    data->thread_qps[available_qp].depth = 1;
364
0
    data->thread_qps[available_qp].lock = lock;
365
0
    return 1;
366
0
}
367
368
void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock)
369
0
{
370
0
    int i;
371
0
    struct rcu_thr_data *data = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx);
372
0
    uint64_t ret;
373
374
0
    assert(data != NULL);
375
376
0
    for (i = 0; i < MAX_QPS; i++) {
377
0
        if (data->thread_qps[i].lock == lock) {
378
            /*
379
             * we have to use __ATOMIC_RELEASE here
380
             * to ensure that all preceding read instructions complete
381
             * before the decrement is visible to ossl_synchronize_rcu
382
             */
383
0
            data->thread_qps[i].depth--;
384
0
            if (data->thread_qps[i].depth == 0) {
385
0
                ret = ATOMIC_SUB_FETCH(&data->thread_qps[i].qp->users,
386
0
                    (uint64_t)1, __ATOMIC_RELEASE);
387
0
                OPENSSL_assert(ret != UINT64_MAX);
388
0
                data->thread_qps[i].qp = NULL;
389
0
                data->thread_qps[i].lock = NULL;
390
0
            }
391
0
            return;
392
0
        }
393
0
    }
394
    /*
395
     * If we get here, we're trying to unlock a lock that we never acquired -
396
     * that's fatal.
397
     */
398
0
    assert(0);
399
0
}
400
401
/*
402
 * Write side allocation routine to get the current qp
403
 * and replace it with a new one
404
 */
405
static struct rcu_qp *update_qp(CRYPTO_RCU_LOCK *lock, uint32_t *curr_id)
406
0
{
407
0
    uint32_t current_idx;
408
409
0
    pthread_mutex_lock(&lock->alloc_lock);
410
411
    /*
412
     * we need at least one qp to be available with one
413
     * left over, so that readers can start working on
414
     * one that isn't yet being waited on
415
     */
416
0
    while (lock->group_count - lock->writers_alloced < 2)
417
        /* we have to wait for one to be free */
418
0
        pthread_cond_wait(&lock->alloc_signal, &lock->alloc_lock);
419
420
0
    current_idx = lock->current_alloc_idx;
421
422
    /* Allocate the qp */
423
0
    lock->writers_alloced++;
424
425
    /* increment the allocation index */
426
0
    lock->current_alloc_idx = (lock->current_alloc_idx + 1) % lock->group_count;
427
428
0
    *curr_id = lock->id_ctr;
429
0
    lock->id_ctr++;
430
431
    /*
432
     * make the current state of everything visible by this release
433
     * when get_hold_current_qp acquires the next qp
434
     */
435
0
    ATOMIC_STORE_N(uint32_t, &lock->reader_idx, lock->current_alloc_idx,
436
0
        __ATOMIC_RELEASE);
437
438
    /*
439
     * this should make sure that the new value of reader_idx is visible in
440
     * get_hold_current_qp, directly after incrementing the users count
441
     */
442
0
    ATOMIC_ADD_FETCH(&lock->qp_group[current_idx].users, (uint64_t)0,
443
0
        __ATOMIC_RELEASE);
444
445
    /* wake up any waiters */
446
0
    pthread_cond_signal(&lock->alloc_signal);
447
0
    pthread_mutex_unlock(&lock->alloc_lock);
448
0
    return &lock->qp_group[current_idx];
449
0
}
450
451
static void retire_qp(CRYPTO_RCU_LOCK *lock, struct rcu_qp *qp)
452
0
{
453
0
    pthread_mutex_lock(&lock->alloc_lock);
454
0
    lock->writers_alloced--;
455
0
    pthread_cond_signal(&lock->alloc_signal);
456
0
    pthread_mutex_unlock(&lock->alloc_lock);
457
0
}
458
459
static struct rcu_qp *allocate_new_qp_group(CRYPTO_RCU_LOCK *lock,
460
    uint32_t count)
461
9
{
462
9
    struct rcu_qp *new = OPENSSL_calloc(count, sizeof(*new));
463
464
9
    lock->group_count = count;
465
9
    return new;
466
9
}
467
468
void ossl_rcu_write_lock(CRYPTO_RCU_LOCK *lock)
469
0
{
470
0
    pthread_mutex_lock(&lock->write_lock);
471
0
    TSAN_FAKE_UNLOCK(&lock->write_lock);
472
0
}
473
474
void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock)
475
0
{
476
0
    TSAN_FAKE_LOCK(&lock->write_lock);
477
0
    pthread_mutex_unlock(&lock->write_lock);
478
0
}
479
480
void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock)
481
0
{
482
0
    struct rcu_qp *qp;
483
0
    uint64_t count;
484
0
    uint32_t curr_id;
485
0
    struct rcu_cb_item *cb_items, *tmpcb;
486
487
0
    pthread_mutex_lock(&lock->write_lock);
488
0
    cb_items = lock->cb_items;
489
0
    lock->cb_items = NULL;
490
0
    pthread_mutex_unlock(&lock->write_lock);
491
492
0
    qp = update_qp(lock, &curr_id);
493
494
    /* retire in order */
495
0
    pthread_mutex_lock(&lock->prior_lock);
496
0
    while (lock->next_to_retire != curr_id)
497
0
        pthread_cond_wait(&lock->prior_signal, &lock->prior_lock);
498
499
    /*
500
     * wait for the reader count to reach zero
501
     * Note the use of __ATOMIC_ACQUIRE here to ensure that any
502
     * prior __ATOMIC_RELEASE write operation in ossl_rcu_read_unlock
503
     * is visible prior to our read
504
     * however this is likely just necessary to silence a tsan warning
505
     * because the read side should not do any write operation
506
     * outside the atomic itself
507
     */
508
0
    do {
509
0
        count = ATOMIC_LOAD_N(uint64_t, &qp->users, __ATOMIC_ACQUIRE);
510
0
    } while (count != (uint64_t)0);
511
512
0
    lock->next_to_retire++;
513
0
    pthread_cond_broadcast(&lock->prior_signal);
514
0
    pthread_mutex_unlock(&lock->prior_lock);
515
516
0
    retire_qp(lock, qp);
517
518
    /* handle any callbacks that we have */
519
0
    while (cb_items != NULL) {
520
0
        tmpcb = cb_items;
521
0
        cb_items = cb_items->next;
522
0
        tmpcb->fn(tmpcb->data);
523
0
        OPENSSL_free(tmpcb);
524
0
    }
525
0
}
526
527
CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void)
528
0
{
529
0
    return OPENSSL_zalloc(sizeof(CRYPTO_RCU_CB_ITEM));
530
0
}
531
532
void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item)
533
0
{
534
0
    OPENSSL_free(item);
535
0
}
536
537
/*
538
 * Note: This call assumes its made under the protection of
539
 * ossl_rcu_write_lock
540
 */
541
void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
542
    rcu_cb_fn cb, void *data)
543
0
{
544
0
    item->fn = cb;
545
0
    item->data = data;
546
0
    item->next = lock->cb_items;
547
0
    lock->cb_items = item;
548
0
}
549
550
void *ossl_rcu_uptr_deref(void **p)
551
2.77M
{
552
2.77M
    return ATOMIC_LOAD_N(pvoid, p, __ATOMIC_ACQUIRE);
553
2.77M
}
554
555
void ossl_rcu_assign_uptr(void **p, void **v)
556
1.02k
{
557
1.02k
    ATOMIC_STORE(pvoid, p, v, __ATOMIC_RELEASE);
558
1.02k
}
559
560
CRYPTO_RCU_LOCK *ossl_rcu_lock_new(int num_writers, OSSL_LIB_CTX *ctx)
561
9
{
562
9
    struct rcu_lock_st *new;
563
9
    pthread_mutex_t *mutexes[3] = { NULL };
564
9
    pthread_cond_t *conds[2] = { NULL };
565
9
    int i;
566
567
    /*
568
     * We need a minimum of 2 qp's
569
     */
570
9
    if (num_writers < 2)
571
9
        num_writers = 2;
572
573
9
    ctx = ossl_lib_ctx_get_concrete(ctx);
574
9
    if (ctx == NULL)
575
0
        return 0;
576
577
9
    new = OPENSSL_zalloc(sizeof(*new));
578
9
    if (new == NULL)
579
0
        return NULL;
580
581
9
    new->ctx = ctx;
582
9
    i = 0;
583
9
    mutexes[i] = pthread_mutex_init(&new->write_lock, NULL) == 0 ? &new->write_lock : NULL;
584
9
    if (mutexes[i++] == NULL)
585
0
        goto err;
586
9
    mutexes[i] = pthread_mutex_init(&new->prior_lock, NULL) == 0 ? &new->prior_lock : NULL;
587
9
    if (mutexes[i++] == NULL)
588
0
        goto err;
589
9
    mutexes[i] = pthread_mutex_init(&new->alloc_lock, NULL) == 0 ? &new->alloc_lock : NULL;
590
9
    if (mutexes[i++] == NULL)
591
0
        goto err;
592
9
    conds[i - 3] = pthread_cond_init(&new->prior_signal, NULL) == 0 ? &new->prior_signal : NULL;
593
9
    if (conds[i - 3] == NULL)
594
0
        goto err;
595
9
    i++;
596
9
    conds[i - 3] = pthread_cond_init(&new->alloc_signal, NULL) == 0 ? &new->alloc_signal : NULL;
597
9
    if (conds[i - 3] == NULL)
598
0
        goto err;
599
9
    i++;
600
9
    new->qp_group = allocate_new_qp_group(new, num_writers);
601
9
    if (new->qp_group == NULL)
602
0
        goto err;
603
604
9
    return new;
605
606
0
err:
607
0
    for (i = 0; i < 3; i++)
608
0
        if (mutexes[i] != NULL)
609
0
            pthread_mutex_destroy(mutexes[i]);
610
0
    for (i = 0; i < 2; i++)
611
0
        if (conds[i] != NULL)
612
0
            pthread_cond_destroy(conds[i]);
613
0
    OPENSSL_free(new->qp_group);
614
0
    OPENSSL_free(new);
615
0
    return NULL;
616
9
}
617
618
void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock)
619
0
{
620
0
    struct rcu_lock_st *rlock = (struct rcu_lock_st *)lock;
621
622
0
    if (lock == NULL)
623
0
        return;
624
625
    /* make sure we're synchronized */
626
0
    ossl_synchronize_rcu(rlock);
627
628
0
    OPENSSL_free(rlock->qp_group);
629
    /*
630
     * Some targets (BSD) allocate heap when initializing
631
     * a mutex or condition, to prevent leaks, those need
632
     * to be destroyed here
633
     */
634
0
    pthread_mutex_destroy(&rlock->write_lock);
635
0
    pthread_mutex_destroy(&rlock->prior_lock);
636
0
    pthread_mutex_destroy(&rlock->alloc_lock);
637
0
    pthread_cond_destroy(&rlock->prior_signal);
638
0
    pthread_cond_destroy(&rlock->alloc_signal);
639
640
    /* There should only be a single qp left now */
641
0
    OPENSSL_free(rlock);
642
0
}
643
644
#ifdef REPORT_RWLOCK_CONTENTION
645
/*
646
 * Normally we would use a BIO here to do this, but we create locks during
647
 * library initialization, and creating a bio too early, creates a recursive set
648
 * of stack calls that leads us to call CRYPTO_thread_run_once while currently
649
 * executing the init routine for various run_once functions, which leads to
650
 * deadlock.  Avoid that by just using a FILE pointer.  Also note that we
651
 * directly use a pthread_mutex_t to protect access from multiple threads
652
 * to the contention log file.  We do this because we want to avoid use
653
 * of the CRYPTO_THREAD api so as to prevent recursive blocking reports.
654
 */
655
static CRYPTO_ONCE init_contention_data_flag = CRYPTO_ONCE_STATIC_INIT;
656
pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
657
CRYPTO_THREAD_LOCAL thread_contention_data;
658
659
struct stack_info {
660
    unsigned int nptrs;
661
    int write;
662
    OSSL_TIME start;
663
    OSSL_TIME duration;
664
    char **strings;
665
};
666
667
#define STACKS_COUNT 32
668
#define BT_BUF_SIZE 1024
669
struct stack_traces {
670
    int fd;
671
    int lock_depth;
672
    size_t idx;
673
    struct stack_info stacks[STACKS_COUNT];
674
};
675
676
/* The glibc gettid() definition presents only since 2.30. */
677
static ossl_inline pid_t get_tid(void)
678
{
679
#ifdef OPENSSL_SYS_MACOSX
680
    /*
681
     * MACOS has the gettid call, but it does something completely different
682
     * here than on other unixes.  Specifically it returns the uid of the calling thread
683
     * (if set), or -1.  We need to use a MACOS specific call to get the thread id here
684
     */
685
    uint64_t tid;
686
687
    pthread_threadid_np(NULL, &tid);
688
    return (pid_t)tid;
689
#else
690
    return syscall(SYS_gettid);
691
#endif
692
}
693
694
#ifdef FIPS_MODULE
695
#define FIPS_SFX "-fips"
696
#else
697
#define FIPS_SFX ""
698
#endif
699
static void *init_contention_data(void)
700
{
701
    struct stack_traces *traces;
702
    char fname[PATH_MAX];
703
    const char *log_dir;
704
    const char *dir;
705
706
    traces = OPENSSL_zalloc(sizeof(struct stack_traces));
707
708
    log_dir = getenv("OPENSSL_LOCK_CONTENTION_DIR");
709
    dir = (log_dir != NULL && log_dir[0] != '\0') ? log_dir : ".";
710
    snprintf(fname, sizeof(fname), "%s/lock-contention-log" FIPS_SFX ".%d.txt",
711
        dir, get_tid());
712
713
    traces->fd = open(fname, O_WRONLY | O_APPEND | O_CLOEXEC | O_CREAT, 0600);
714
715
    return traces;
716
}
717
718
static void destroy_contention_data(void *data)
719
{
720
    struct stack_traces *st = data;
721
722
    close(st->fd);
723
    OPENSSL_free(data);
724
}
725
726
static void init_contention_data_once(void)
727
{
728
    /*
729
     * Create a thread local key here to store our list of stack traces
730
     * to be printed when we unlock the lock we are holding
731
     */
732
    CRYPTO_THREAD_init_local(&thread_contention_data, destroy_contention_data);
733
    return;
734
}
735
736
static struct stack_traces *get_stack_traces(bool init)
737
{
738
    struct stack_traces *traces = CRYPTO_THREAD_get_local(&thread_contention_data);
739
740
    if (!traces && init) {
741
        traces = init_contention_data();
742
        CRYPTO_THREAD_set_local(&thread_contention_data, traces);
743
    }
744
745
    return traces;
746
}
747
748
static void print_stack_traces(struct stack_traces *traces)
749
{
750
    unsigned int j;
751
    struct iovec *iov;
752
    int iovcnt;
753
754
    while (traces != NULL && traces->idx >= 1) {
755
        traces->idx--;
756
        dprintf(traces->fd,
757
            "lock blocked on %s for %zu usec at time %zu tid %d\n",
758
            traces->stacks[traces->idx].write == 1 ? "WRITE" : "READ",
759
            ossl_time2us(traces->stacks[traces->idx].duration),
760
            ossl_time2us(traces->stacks[traces->idx].start),
761
            get_tid());
762
        if (traces->stacks[traces->idx].strings != NULL) {
763
            static const char lf = '\n';
764
765
            iovcnt = traces->stacks[traces->idx].nptrs * 2 + 1;
766
            iov = alloca(iovcnt * sizeof(*iov));
767
            for (j = 0; j < traces->stacks[traces->idx].nptrs; j++) {
768
                iov[2 * j].iov_base = traces->stacks[traces->idx].strings[j];
769
                iov[2 * j].iov_len = strlen(traces->stacks[traces->idx].strings[j]);
770
                iov[2 * j + 1].iov_base = (char *)&lf;
771
                iov[2 * j + 1].iov_len = 1;
772
            }
773
            iov[traces->stacks[traces->idx].nptrs * 2].iov_base = (char *)&lf;
774
            iov[traces->stacks[traces->idx].nptrs * 2].iov_len = 1;
775
        } else {
776
            static const char no_bt[] = "No stack trace available\n\n";
777
778
            iovcnt = 1;
779
            iov = alloca(iovcnt * sizeof(*iov));
780
            iov[0].iov_base = (char *)no_bt;
781
            iov[0].iov_len = sizeof(no_bt) - 1;
782
        }
783
        writev(traces->fd, iov, iovcnt);
784
        free(traces->stacks[traces->idx].strings);
785
    }
786
}
787
788
static ossl_inline void ossl_init_rwlock_contention_data(void)
789
{
790
    CRYPTO_THREAD_run_once(&init_contention_data_flag, init_contention_data_once);
791
}
792
793
static int record_lock_contention(pthread_rwlock_t *lock,
794
    struct stack_traces *traces, bool write)
795
{
796
    void *buffer[BT_BUF_SIZE];
797
    OSSL_TIME start, end;
798
    int ret;
799
800
    start = ossl_time_now();
801
    ret = (write ? pthread_rwlock_wrlock : pthread_rwlock_rdlock)(lock);
802
    if (ret)
803
        return ret;
804
    end = ossl_time_now();
805
    traces->stacks[traces->idx].nptrs = backtrace(buffer, BT_BUF_SIZE);
806
    traces->stacks[traces->idx].strings = backtrace_symbols(buffer,
807
        traces->stacks[traces->idx].nptrs);
808
    traces->stacks[traces->idx].duration = ossl_time_subtract(end, start);
809
    traces->stacks[traces->idx].start = start;
810
    traces->stacks[traces->idx].write = write;
811
    traces->idx++;
812
    if (traces->idx >= STACKS_COUNT) {
813
        fprintf(stderr, "STACK RECORD OVERFLOW!\n");
814
        print_stack_traces(traces);
815
    }
816
817
    return 0;
818
}
819
820
static ossl_inline int ossl_rwlock_rdlock(pthread_rwlock_t *lock)
821
{
822
    struct stack_traces *traces = get_stack_traces(true);
823
824
    if (ossl_unlikely(traces == NULL))
825
        return ENOMEM;
826
827
    traces->lock_depth++;
828
    if (pthread_rwlock_tryrdlock(lock)) {
829
        int ret = record_lock_contention(lock, traces, false);
830
831
        if (ret)
832
            traces->lock_depth--;
833
834
        return ret;
835
    }
836
837
    return 0;
838
}
839
840
static ossl_inline int ossl_rwlock_wrlock(pthread_rwlock_t *lock)
841
{
842
    struct stack_traces *traces = get_stack_traces(true);
843
844
    if (ossl_unlikely(traces == NULL))
845
        return ENOMEM;
846
847
    traces->lock_depth++;
848
    if (pthread_rwlock_trywrlock(lock)) {
849
        int ret = record_lock_contention(lock, traces, true);
850
851
        if (ret)
852
            traces->lock_depth--;
853
854
        return ret;
855
    }
856
857
    return 0;
858
}
859
860
static ossl_inline int ossl_rwlock_unlock(pthread_rwlock_t *lock)
861
{
862
    int ret;
863
864
    ret = pthread_rwlock_unlock(lock);
865
    if (ret)
866
        return ret;
867
868
    {
869
        struct stack_traces *traces = get_stack_traces(false);
870
871
        if (traces != NULL) {
872
            traces->lock_depth--;
873
            assert(traces->lock_depth >= 0);
874
            if (traces->lock_depth == 0)
875
                print_stack_traces(traces);
876
        }
877
    }
878
879
    return 0;
880
}
881
882
#else /* !REPORT_RWLOCK_CONTENTION */
883
884
#if defined(USE_RWLOCK)
885
static ossl_inline void ossl_init_rwlock_contention_data(void)
886
477
{
887
477
}
888
889
static ossl_inline int ossl_rwlock_rdlock(pthread_rwlock_t *rwlock)
890
9.42k
{
891
9.42k
    return pthread_rwlock_rdlock(rwlock);
892
9.42k
}
893
894
static ossl_inline int ossl_rwlock_wrlock(pthread_rwlock_t *rwlock)
895
6.69k
{
896
6.69k
    return pthread_rwlock_wrlock(rwlock);
897
6.69k
}
898
899
static ossl_inline int ossl_rwlock_unlock(pthread_rwlock_t *rwlock)
900
16.1k
{
901
16.1k
    return pthread_rwlock_unlock(rwlock);
902
16.1k
}
903
#endif /* USE_RWLOCK */
904
#endif /* REPORT_RWLOCK_CONTENTION */
905
906
CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
907
477
{
908
477
#ifdef USE_RWLOCK
909
477
    CRYPTO_RWLOCK *lock;
910
911
477
    ossl_init_rwlock_contention_data();
912
913
477
    if ((lock = OPENSSL_zalloc(sizeof(pthread_rwlock_t))) == NULL)
914
        /* Don't set error, to avoid recursion blowup. */
915
0
        return NULL;
916
917
477
    if (pthread_rwlock_init(lock, NULL) != 0) {
918
0
        OPENSSL_free(lock);
919
0
        return NULL;
920
0
    }
921
#else
922
    pthread_mutexattr_t attr;
923
    CRYPTO_RWLOCK *lock;
924
925
    if ((lock = OPENSSL_zalloc(sizeof(pthread_mutex_t))) == NULL)
926
        /* Don't set error, to avoid recursion blowup. */
927
        return NULL;
928
929
    /*
930
     * We don't use recursive mutexes, but try to catch errors if we do.
931
     */
932
    pthread_mutexattr_init(&attr);
933
#if !defined(__TANDEM) && !defined(_SPT_MODEL_)
934
#if !defined(NDEBUG) && !defined(OPENSSL_NO_MUTEX_ERRORCHECK)
935
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
936
#endif
937
#else
938
    /* The SPT Thread Library does not define MUTEX attributes. */
939
#endif
940
941
    if (pthread_mutex_init(lock, &attr) != 0) {
942
        pthread_mutexattr_destroy(&attr);
943
        OPENSSL_free(lock);
944
        return NULL;
945
    }
946
947
    pthread_mutexattr_destroy(&attr);
948
#endif
949
950
477
    return lock;
951
477
}
952
953
__owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
954
9.42k
{
955
9.42k
#ifdef USE_RWLOCK
956
9.42k
    if (!ossl_assert(ossl_rwlock_rdlock(lock) == 0))
957
0
        return 0;
958
#else
959
    if (pthread_mutex_lock(lock) != 0) {
960
        assert(errno != EDEADLK && errno != EBUSY);
961
        return 0;
962
    }
963
#endif
964
965
9.42k
    return 1;
966
9.42k
}
967
968
__owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
969
6.69k
{
970
6.69k
#ifdef USE_RWLOCK
971
6.69k
    if (!ossl_assert(ossl_rwlock_wrlock(lock) == 0))
972
0
        return 0;
973
#else
974
    if (pthread_mutex_lock(lock) != 0) {
975
        assert(errno != EDEADLK && errno != EBUSY);
976
        return 0;
977
    }
978
#endif
979
980
6.69k
    return 1;
981
6.69k
}
982
983
int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
984
16.1k
{
985
16.1k
#ifdef USE_RWLOCK
986
16.1k
    if (ossl_rwlock_unlock(lock) != 0)
987
0
        return 0;
988
#else
989
    if (pthread_mutex_unlock(lock) != 0) {
990
        assert(errno != EPERM);
991
        return 0;
992
    }
993
#endif
994
995
16.1k
    return 1;
996
16.1k
}
997
998
void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
999
0
{
1000
0
    if (lock == NULL)
1001
0
        return;
1002
1003
0
#ifdef USE_RWLOCK
1004
0
    pthread_rwlock_destroy(lock);
1005
#else
1006
    pthread_mutex_destroy(lock);
1007
#endif
1008
0
    OPENSSL_free(lock);
1009
1010
0
    return;
1011
0
}
1012
1013
int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
1014
15.5k
{
1015
15.5k
    if (ossl_unlikely(pthread_once(once, init) != 0))
1016
0
        return 0;
1017
1018
15.5k
    return 1;
1019
15.5k
}
1020
1021
int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
1022
12
{
1023
12
    if (pthread_key_create(key, cleanup) != 0)
1024
0
        return 0;
1025
1026
12
    return 1;
1027
12
}
1028
1029
void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
1030
6.43k
{
1031
6.43k
    return pthread_getspecific(*key);
1032
6.43k
}
1033
1034
int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
1035
6
{
1036
6
    if (pthread_setspecific(*key, val) != 0)
1037
0
        return 0;
1038
1039
6
    return 1;
1040
6
}
1041
1042
int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
1043
0
{
1044
0
    if (pthread_key_delete(*key) != 0)
1045
0
        return 0;
1046
1047
0
    return 1;
1048
0
}
1049
1050
CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
1051
0
{
1052
0
    return pthread_self();
1053
0
}
1054
1055
int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
1056
0
{
1057
0
    return pthread_equal(a, b);
1058
0
}
1059
1060
int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
1061
4.73k
{
1062
4.73k
#if defined(OSSL_USE_GCC_ATOMICS)
1063
4.73k
    if (__atomic_is_lock_free(sizeof(*val), val)) {
1064
4.73k
        *ret = __atomic_add_fetch(val, amount, __ATOMIC_ACQ_REL);
1065
4.73k
        return 1;
1066
4.73k
    }
1067
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1068
    /* This will work for all future Solaris versions. */
1069
    if (ret != NULL) {
1070
        *ret = atomic_add_int_nv((volatile unsigned int *)val, amount);
1071
        return 1;
1072
    }
1073
#endif
1074
0
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1075
0
        return 0;
1076
1077
0
    *val += amount;
1078
0
    *ret = *val;
1079
1080
0
    if (!CRYPTO_THREAD_unlock(lock))
1081
0
        return 0;
1082
1083
0
    return 1;
1084
0
}
1085
1086
int CRYPTO_atomic_add64(uint64_t *val, uint64_t op, uint64_t *ret,
1087
    CRYPTO_RWLOCK *lock)
1088
0
{
1089
0
#if defined(OSSL_USE_GCC_ATOMICS)
1090
0
    if (__atomic_is_lock_free(sizeof(*val), val)) {
1091
0
        *ret = __atomic_add_fetch(val, op, __ATOMIC_ACQ_REL);
1092
0
        return 1;
1093
0
    }
1094
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1095
    /* This will work for all future Solaris versions. */
1096
    if (ret != NULL) {
1097
        *ret = atomic_add_64_nv(val, op);
1098
        return 1;
1099
    }
1100
#endif
1101
0
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1102
0
        return 0;
1103
0
    *val += op;
1104
0
    *ret = *val;
1105
1106
0
    if (!CRYPTO_THREAD_unlock(lock))
1107
0
        return 0;
1108
1109
0
    return 1;
1110
0
}
1111
1112
int CRYPTO_atomic_and(uint64_t *val, uint64_t op, uint64_t *ret,
1113
    CRYPTO_RWLOCK *lock)
1114
0
{
1115
0
#if defined(OSSL_USE_GCC_ATOMICS)
1116
0
    if (__atomic_is_lock_free(sizeof(*val), val)) {
1117
0
        *ret = __atomic_and_fetch(val, op, __ATOMIC_ACQ_REL);
1118
0
        return 1;
1119
0
    }
1120
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1121
    /* This will work for all future Solaris versions. */
1122
    if (ret != NULL) {
1123
        *ret = atomic_and_64_nv(val, op);
1124
        return 1;
1125
    }
1126
#endif
1127
0
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1128
0
        return 0;
1129
0
    *val &= op;
1130
0
    *ret = *val;
1131
1132
0
    if (!CRYPTO_THREAD_unlock(lock))
1133
0
        return 0;
1134
1135
0
    return 1;
1136
0
}
1137
1138
int CRYPTO_atomic_or(uint64_t *val, uint64_t op, uint64_t *ret,
1139
    CRYPTO_RWLOCK *lock)
1140
6
{
1141
6
#if defined(OSSL_USE_GCC_ATOMICS)
1142
6
    if (__atomic_is_lock_free(sizeof(*val), val)) {
1143
6
        *ret = __atomic_or_fetch(val, op, __ATOMIC_ACQ_REL);
1144
6
        return 1;
1145
6
    }
1146
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1147
    /* This will work for all future Solaris versions. */
1148
    if (ret != NULL) {
1149
        *ret = atomic_or_64_nv(val, op);
1150
        return 1;
1151
    }
1152
#endif
1153
0
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1154
0
        return 0;
1155
0
    *val |= op;
1156
0
    *ret = *val;
1157
1158
0
    if (!CRYPTO_THREAD_unlock(lock))
1159
0
        return 0;
1160
1161
0
    return 1;
1162
0
}
1163
1164
int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock)
1165
1.93M
{
1166
1.93M
#if defined(OSSL_USE_GCC_ATOMICS)
1167
1.93M
    if (__atomic_is_lock_free(sizeof(*val), val)) {
1168
1.93M
        __atomic_load(val, ret, __ATOMIC_ACQUIRE);
1169
1.93M
        return 1;
1170
1.93M
    }
1171
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1172
    /* This will work for all future Solaris versions. */
1173
    if (ret != NULL) {
1174
        *ret = atomic_or_64_nv(val, 0);
1175
        return 1;
1176
    }
1177
#endif
1178
0
    if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
1179
0
        return 0;
1180
0
    *ret = *val;
1181
0
    if (!CRYPTO_THREAD_unlock(lock))
1182
0
        return 0;
1183
1184
0
    return 1;
1185
0
}
1186
1187
int CRYPTO_atomic_store(uint64_t *dst, uint64_t val, CRYPTO_RWLOCK *lock)
1188
1.02k
{
1189
1.02k
#if defined(OSSL_USE_GCC_ATOMICS)
1190
1.02k
    if (__atomic_is_lock_free(sizeof(*dst), dst)) {
1191
1.02k
        __atomic_store(dst, &val, __ATOMIC_RELEASE);
1192
1.02k
        return 1;
1193
1.02k
    }
1194
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1195
    /* This will work for all future Solaris versions. */
1196
    if (dst != NULL) {
1197
        atomic_swap_64(dst, val);
1198
        return 1;
1199
    }
1200
#endif
1201
0
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1202
0
        return 0;
1203
0
    *dst = val;
1204
0
    if (!CRYPTO_THREAD_unlock(lock))
1205
0
        return 0;
1206
1207
0
    return 1;
1208
0
}
1209
1210
int CRYPTO_atomic_load_int(int *val, int *ret, CRYPTO_RWLOCK *lock)
1211
27.0M
{
1212
27.0M
#if defined(OSSL_USE_GCC_ATOMICS)
1213
27.0M
    if (__atomic_is_lock_free(sizeof(*val), val)) {
1214
27.0M
        __atomic_load(val, ret, __ATOMIC_ACQUIRE);
1215
27.0M
        return 1;
1216
27.0M
    }
1217
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1218
    /* This will work for all future Solaris versions. */
1219
    if (ret != NULL) {
1220
        *ret = (int)atomic_or_uint_nv((unsigned int *)val, 0);
1221
        return 1;
1222
    }
1223
#endif
1224
0
    if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
1225
0
        return 0;
1226
0
    *ret = *val;
1227
0
    if (!CRYPTO_THREAD_unlock(lock))
1228
0
        return 0;
1229
1230
0
    return 1;
1231
0
}
1232
1233
int CRYPTO_atomic_store_int(int *dst, int val, CRYPTO_RWLOCK *lock)
1234
0
{
1235
0
#if defined(OSSL_USE_GCC_ATOMICS)
1236
0
    if (__atomic_is_lock_free(sizeof(*dst), dst)) {
1237
0
        __atomic_store(dst, &val, __ATOMIC_RELEASE);
1238
0
        return 1;
1239
0
    }
1240
#elif defined(OSSL_USE_SOLARIS_ATOMICS)
1241
    /* This will work for all future Solaris versions. */
1242
    if (dst != NULL) {
1243
        atomic_swap_uint((unsigned int)dst, (unsigned int)val);
1244
        return 1;
1245
    }
1246
#endif
1247
0
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1248
0
        return 0;
1249
0
    *dst = val;
1250
0
    if (!CRYPTO_THREAD_unlock(lock))
1251
0
        return 0;
1252
1253
0
    return 1;
1254
0
}
1255
1256
int CRYPTO_atomic_load_ptr(void **ptr, void **ret, CRYPTO_RWLOCK *lock)
1257
27.0M
{
1258
27.0M
#if defined(__GNUC__) && defined(__ATOMIC_RELAXED) && !defined(BROKEN_CLANG_ATOMICS)
1259
27.0M
    *ret = __atomic_load_n(ptr, TSAN_LOAD_MEM_ORDER);
1260
27.0M
    return 1;
1261
#else
1262
    if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
1263
        return 0;
1264
    *ret = *ptr;
1265
    if (!CRYPTO_THREAD_unlock(lock))
1266
        return 0;
1267
    return 1;
1268
#endif
1269
27.0M
}
1270
1271
int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
1272
100
{
1273
100
#if defined(__GNUC__) && defined(__ATOMIC_RELAXED) && !defined(BROKEN_CLANG_ATOMICS)
1274
100
    __atomic_store(dst, val, TSAN_STORE_MEM_ORDER);
1275
100
    return 1;
1276
#else
1277
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
1278
        return 0;
1279
    *dst = *val;
1280
    if (!CRYPTO_THREAD_unlock(lock))
1281
        return 0;
1282
    return 1;
1283
#endif
1284
100
}
1285
1286
int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock, int *lock_failed)
1287
100
{
1288
100
#if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS)
1289
100
    if (lock_failed != NULL)
1290
100
        *lock_failed = 0;
1291
100
    return __atomic_compare_exchange_n(ptr, expect, desire, 0, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) ? 1 : 0;
1292
#else
1293
    int lock_sink;
1294
    int ret = 0;
1295
1296
    if (lock_failed == NULL)
1297
        lock_failed = &lock_sink;
1298
1299
    *lock_failed = 0;
1300
    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) {
1301
        *lock_failed = 1;
1302
        return 0;
1303
    }
1304
    if (*ptr == *expect) {
1305
        ret = 1;
1306
        *ptr = desire;
1307
    } else {
1308
        *expect = *ptr;
1309
    }
1310
    if (!CRYPTO_THREAD_unlock(lock))
1311
        *lock_failed = 1;
1312
    return ret;
1313
#endif
1314
100
}
1315
1316
#ifndef FIPS_MODULE
1317
int openssl_init_fork_handlers(void)
1318
0
{
1319
0
    return 1;
1320
0
}
1321
#endif /* FIPS_MODULE */
1322
1323
int openssl_get_fork_id(void)
1324
0
{
1325
0
    return getpid();
1326
0
}
1327
#endif