Coverage Report

Created: 2026-06-18 06:34

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