]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/threads_win.c
Fix duplicate mutex allocation in threads_win.c
[thirdparty/openssl.git] / crypto / threads_win.c
CommitLineData
aa6bb135 1/*
b6461792 2 * Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
71a04cfc 3 *
0e9725bc 4 * Licensed under the Apache License 2.0 (the "License"). You may not use
aa6bb135
RS
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
71a04cfc
AG
8 */
9
f1f5ee17
AP
10#if defined(_WIN32)
11# include <windows.h>
f70863d9 12# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
f70863d9
VD
13# define USE_RWLOCK
14# endif
f1f5ee17 15#endif
d0e1a0ae 16#include <assert.h>
f1f5ee17 17
2d46a44f
DN
18/*
19 * VC++ 2008 or earlier x86 compilers do not have an inline implementation
20 * of InterlockedOr64 for 32bit and will fail to run on Windows XP 32bit.
21 * https://docs.microsoft.com/en-us/cpp/intrinsics/interlockedor-intrinsic-functions#requirements
22 * To work around this problem, we implement a manual locking mechanism for
23 * only VC++ 2008 or earlier x86 compilers.
24 */
25
8bdc3708 26#if (defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER <= 1600)
2d46a44f
DN
27# define NO_INTERLOCKEDOR64
28#endif
29
71a04cfc 30#include <openssl/crypto.h>
d0e1a0ae
NH
31#include <crypto/cryptlib.h>
32#include "internal/common.h"
33#include "internal/thread_arch.h"
34#include "internal/rcu.h"
35#include "rcu_internal.h"
71a04cfc
AG
36
37#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && defined(OPENSSL_SYS_WINDOWS)
38
f70863d9
VD
39# ifdef USE_RWLOCK
40typedef struct {
41 SRWLOCK lock;
42 int exclusive;
43} CRYPTO_win_rwlock;
44# endif
45
d0e1a0ae
NH
46static CRYPTO_THREAD_LOCAL rcu_thr_key;
47
48# define READER_SHIFT 0
49# define ID_SHIFT 32
50# define READER_SIZE 32
51# define ID_SIZE 32
52
53# define READER_MASK (((LONG64)1 << READER_SIZE)-1)
54# define ID_MASK (((LONG64)1 << ID_SIZE)-1)
55# define READER_COUNT(x) (((LONG64)(x) >> READER_SHIFT) & READER_MASK)
56# define ID_VAL(x) (((LONG64)(x) >> ID_SHIFT) & ID_MASK)
57# define VAL_READER ((LONG64)1 << READER_SHIFT)
58# define VAL_ID(x) ((LONG64)x << ID_SHIFT)
59
60/*
61 * This defines a quescent point (qp)
62 * This is the barrier beyond which a writer
63 * must wait before freeing data that was
64 * atomically updated
65 */
66struct rcu_qp {
67 volatile LONG64 users;
68};
69
70struct thread_qp {
71 struct rcu_qp *qp;
72 unsigned int depth;
73 CRYPTO_RCU_LOCK *lock;
74};
75
76#define MAX_QPS 10
77/*
78 * This is the per thread tracking data
79 * that is assigned to each thread participating
80 * in an rcu qp
81 *
82 * qp points to the qp that it last acquired
83 *
84 */
85struct rcu_thr_data {
86 struct thread_qp thread_qps[MAX_QPS];
87};
88
89/*
90 * This is the internal version of a CRYPTO_RCU_LOCK
91 * it is cast from CRYPTO_RCU_LOCK
92 */
93struct rcu_lock_st {
94 struct rcu_cb_item *cb_items;
95 uint32_t id_ctr;
96 struct rcu_qp *qp_group;
97 size_t group_count;
98 uint32_t next_to_retire;
99 volatile long int reader_idx;
100 uint32_t current_alloc_idx;
101 uint32_t writers_alloced;
102 CRYPTO_MUTEX *write_lock;
103 CRYPTO_MUTEX *alloc_lock;
104 CRYPTO_CONDVAR *alloc_signal;
105 CRYPTO_MUTEX *prior_lock;
106 CRYPTO_CONDVAR *prior_signal;
107};
108
109/*
110 * Called on thread exit to free the pthread key
111 * associated with this thread, if any
112 */
113static void free_rcu_thr_data(void *ptr)
114{
115 struct rcu_thr_data *data =
116 (struct rcu_thr_data *)CRYPTO_THREAD_get_local(&rcu_thr_key);
117
118 OPENSSL_free(data);
119 CRYPTO_THREAD_set_local(&rcu_thr_key, NULL);
120}
121
122
123static void ossl_rcu_init(void)
124{
125 CRYPTO_THREAD_init_local(&rcu_thr_key, NULL);
126 ossl_init_thread_start(NULL, NULL, free_rcu_thr_data);
127}
128
129static struct rcu_qp *allocate_new_qp_group(struct rcu_lock_st *lock,
130 int count)
131{
132 struct rcu_qp *new =
133 OPENSSL_zalloc(sizeof(*new) * count);
134
135 lock->group_count = count;
136 return new;
137}
138
139static CRYPTO_ONCE rcu_init_once = CRYPTO_ONCE_STATIC_INIT;
140
141CRYPTO_RCU_LOCK *ossl_rcu_lock_new(int num_writers)
142{
143 struct rcu_lock_st *new;
144
145 if (!CRYPTO_THREAD_run_once(&rcu_init_once, ossl_rcu_init))
146 return NULL;
147
148 if (num_writers < 1)
149 num_writers = 1;
150
151 new = OPENSSL_zalloc(sizeof(*new));
152
153 if (new == NULL)
154 return NULL;
155
156 new->write_lock = ossl_crypto_mutex_new();
157 new->alloc_signal = ossl_crypto_condvar_new();
158 new->prior_signal = ossl_crypto_condvar_new();
159 new->alloc_lock = ossl_crypto_mutex_new();
160 new->prior_lock = ossl_crypto_mutex_new();
d0e1a0ae
NH
161 new->qp_group = allocate_new_qp_group(new, num_writers + 1);
162 if (new->qp_group == NULL
163 || new->alloc_signal == NULL
164 || new->prior_signal == NULL
165 || new->write_lock == NULL
166 || new->alloc_lock == NULL
167 || new->prior_lock == NULL) {
168 OPENSSL_free(new->qp_group);
169 ossl_crypto_condvar_free(&new->alloc_signal);
170 ossl_crypto_condvar_free(&new->prior_signal);
171 ossl_crypto_mutex_free(&new->alloc_lock);
172 ossl_crypto_mutex_free(&new->prior_lock);
173 ossl_crypto_mutex_free(&new->write_lock);
174 OPENSSL_free(new);
175 new = NULL;
176 }
177 return new;
178
179}
180
181void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock)
182{
183 OPENSSL_free(lock->qp_group);
184 ossl_crypto_condvar_free(&lock->alloc_signal);
185 ossl_crypto_condvar_free(&lock->prior_signal);
186 ossl_crypto_mutex_free(&lock->alloc_lock);
187 ossl_crypto_mutex_free(&lock->prior_lock);
188 ossl_crypto_mutex_free(&lock->write_lock);
189 OPENSSL_free(lock);
190}
191
192static inline struct rcu_qp *get_hold_current_qp(CRYPTO_RCU_LOCK *lock)
193{
194 uint32_t qp_idx;
195
196 /* get the current qp index */
197 for (;;) {
198 qp_idx = InterlockedOr(&lock->reader_idx, 0);
199 InterlockedAdd64(&lock->qp_group[qp_idx].users, VAL_READER);
200 if (qp_idx == InterlockedOr(&lock->reader_idx, 0))
201 break;
202 InterlockedAdd64(&lock->qp_group[qp_idx].users, -VAL_READER);
203 }
204
205 return &lock->qp_group[qp_idx];
206}
207
208void ossl_rcu_read_lock(CRYPTO_RCU_LOCK *lock)
209{
210 struct rcu_thr_data *data;
211 int i;
212 int available_qp = -1;
213
214 /*
215 * we're going to access current_qp here so ask the
216 * processor to fetch it
217 */
218 data = CRYPTO_THREAD_get_local(&rcu_thr_key);
219
220 if (data == NULL) {
221 data = OPENSSL_zalloc(sizeof(*data));
222 OPENSSL_assert(data != NULL);
223 CRYPTO_THREAD_set_local(&rcu_thr_key, data);
224 }
225
226 for (i = 0; i < MAX_QPS; i++) {
227 if (data->thread_qps[i].qp == NULL && available_qp == -1)
228 available_qp = i;
229 /* If we have a hold on this lock already, we're good */
230 if (data->thread_qps[i].lock == lock)
231 return;
232 }
233
234 /*
235 * if we get here, then we don't have a hold on this lock yet
236 */
237 assert(available_qp != -1);
238
239 data->thread_qps[available_qp].qp = get_hold_current_qp(lock);
240 data->thread_qps[available_qp].depth = 1;
241 data->thread_qps[available_qp].lock = lock;
242}
243
244void ossl_rcu_write_lock(CRYPTO_RCU_LOCK *lock)
245{
246 ossl_crypto_mutex_lock(lock->write_lock);
247}
248
249void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock)
250{
251 ossl_crypto_mutex_unlock(lock->write_lock);
252}
253
254void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock)
255{
256 struct rcu_thr_data *data = CRYPTO_THREAD_get_local(&rcu_thr_key);
257 int i;
258 LONG64 ret;
259
260 assert(data != NULL);
261
262 for (i = 0; i < MAX_QPS; i++) {
263 if (data->thread_qps[i].lock == lock) {
264 data->thread_qps[i].depth--;
265 if (data->thread_qps[i].depth == 0) {
266 ret = InterlockedAdd64(&data->thread_qps[i].qp->users, -VAL_READER);
267 OPENSSL_assert(ret >= 0);
268 data->thread_qps[i].qp = NULL;
269 data->thread_qps[i].lock = NULL;
270 }
271 return;
272 }
273 }
274}
275
276static struct rcu_qp *update_qp(CRYPTO_RCU_LOCK *lock)
277{
278 uint64_t new_id;
279 uint32_t current_idx;
280 uint32_t tmp;
281
282 ossl_crypto_mutex_lock(lock->alloc_lock);
283 /*
284 * we need at least one qp to be available with one
285 * left over, so that readers can start working on
286 * one that isn't yet being waited on
287 */
288 while (lock->group_count - lock->writers_alloced < 2)
289 ossl_crypto_condvar_wait(lock->alloc_signal, lock->alloc_lock);
290
291 current_idx = lock->current_alloc_idx;
292 /* Allocate the qp */
293 lock->writers_alloced++;
294
295 /* increment the allocation index */
296 lock->current_alloc_idx =
297 (lock->current_alloc_idx + 1) % lock->group_count;
298
299 /* get and insert a new id */
300 new_id = lock->id_ctr;
301 lock->id_ctr++;
302
303 new_id = VAL_ID(new_id);
304 InterlockedAnd64(&lock->qp_group[current_idx].users, ID_MASK);
305 InterlockedAdd64(&lock->qp_group[current_idx].users, new_id);
306
307 /* update the reader index to be the prior qp */
308 tmp = lock->current_alloc_idx;
309 InterlockedExchange(&lock->reader_idx, tmp);
310
311 /* wake up any waiters */
312 ossl_crypto_condvar_broadcast(lock->alloc_signal);
313 ossl_crypto_mutex_unlock(lock->alloc_lock);
314 return &lock->qp_group[current_idx];
315}
316
317static void retire_qp(CRYPTO_RCU_LOCK *lock,
318 struct rcu_qp *qp)
319{
320 ossl_crypto_mutex_lock(lock->alloc_lock);
321 lock->writers_alloced--;
322 ossl_crypto_condvar_broadcast(lock->alloc_signal);
323 ossl_crypto_mutex_unlock(lock->alloc_lock);
324}
325
326
327void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock)
328{
329 struct rcu_qp *qp;
330 uint64_t count;
331 struct rcu_cb_item *cb_items, *tmpcb;
332
333 /* before we do anything else, lets grab the cb list */
334 cb_items = InterlockedExchangePointer((void * volatile *)&lock->cb_items, NULL);
335
336 qp = update_qp(lock);
337
338 /* wait for the reader count to reach zero */
339 do {
340 count = InterlockedOr64(&qp->users, 0);
341 } while (READER_COUNT(count) != 0);
342
343 /* retire in order */
344 ossl_crypto_mutex_lock(lock->prior_lock);
345 while (lock->next_to_retire != ID_VAL(count))
346 ossl_crypto_condvar_wait(lock->prior_signal, lock->prior_lock);
347
348 lock->next_to_retire++;
349 ossl_crypto_condvar_broadcast(lock->prior_signal);
350 ossl_crypto_mutex_unlock(lock->prior_lock);
351
352 retire_qp(lock, qp);
353
354 /* handle any callbacks that we have */
355 while (cb_items != NULL) {
356 tmpcb = cb_items;
357 cb_items = cb_items->next;
358 tmpcb->fn(tmpcb->data);
359 OPENSSL_free(tmpcb);
360 }
361
362 /* and we're done */
363 return;
364
365}
366
367int ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data)
368{
369 struct rcu_cb_item *new;
370 struct rcu_cb_item *prev;
371
372 new = OPENSSL_zalloc(sizeof(struct rcu_cb_item));
373 if (new == NULL)
374 return 0;
375 prev = new;
376 new->data = data;
377 new->fn = cb;
378
379 InterlockedExchangePointer((void * volatile *)&lock->cb_items, prev);
380 new->next = prev;
381 return 1;
382}
383
384void *ossl_rcu_uptr_deref(void **p)
385{
386 return (void *)*p;
387}
388
389void ossl_rcu_assign_uptr(void **p, void **v)
390{
391 InterlockedExchangePointer((void * volatile *)p, (void *)*v);
392}
393
394
71a04cfc
AG
395CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
396{
7de2b9c4 397 CRYPTO_RWLOCK *lock;
f70863d9
VD
398# ifdef USE_RWLOCK
399 CRYPTO_win_rwlock *rwlock;
400
d0e1a0ae 401 if ((lock = OPENSSL_zalloc(sizeof(CRYPTO_win_rwlock))) == NULL)
894f2166 402 /* Don't set error, to avoid recursion blowup. */
f70863d9
VD
403 return NULL;
404 rwlock = lock;
405 InitializeSRWLock(&rwlock->lock);
406# else
7de2b9c4 407
d0e1a0ae 408 if ((lock = OPENSSL_zalloc(sizeof(CRITICAL_SECTION))) == NULL)
7de2b9c4 409 /* Don't set error, to avoid recursion blowup. */
71a04cfc
AG
410 return NULL;
411
f70863d9 412# if !defined(_WIN32_WCE)
71a04cfc 413 /* 0x400 is the spin count value suggested in the documentation */
0b2fc928
F
414 if (!InitializeCriticalSectionAndSpinCount(lock, 0x400)) {
415 OPENSSL_free(lock);
71a04cfc 416 return NULL;
0b2fc928 417 }
f70863d9 418# else
09305a7d 419 InitializeCriticalSection(lock);
f70863d9 420# endif
7f0a8dc7 421# endif
71a04cfc
AG
422
423 return lock;
424}
425
cd3f8c1b 426__owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
71a04cfc 427{
f70863d9
VD
428# ifdef USE_RWLOCK
429 CRYPTO_win_rwlock *rwlock = lock;
430
431 AcquireSRWLockShared(&rwlock->lock);
432# else
71a04cfc 433 EnterCriticalSection(lock);
f70863d9 434# endif
71a04cfc
AG
435 return 1;
436}
437
cd3f8c1b 438__owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
71a04cfc 439{
f70863d9
VD
440# ifdef USE_RWLOCK
441 CRYPTO_win_rwlock *rwlock = lock;
442
443 AcquireSRWLockExclusive(&rwlock->lock);
444 rwlock->exclusive = 1;
445# else
71a04cfc 446 EnterCriticalSection(lock);
f70863d9 447# endif
71a04cfc
AG
448 return 1;
449}
450
451int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
452{
f70863d9
VD
453# ifdef USE_RWLOCK
454 CRYPTO_win_rwlock *rwlock = lock;
455
456 if (rwlock->exclusive) {
457 rwlock->exclusive = 0;
458 ReleaseSRWLockExclusive(&rwlock->lock);
459 } else {
460 ReleaseSRWLockShared(&rwlock->lock);
461 }
462# else
71a04cfc 463 LeaveCriticalSection(lock);
f70863d9 464# endif
71a04cfc
AG
465 return 1;
466}
467
468void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
469{
470 if (lock == NULL)
471 return;
472
f70863d9 473# ifndef USE_RWLOCK
71a04cfc 474 DeleteCriticalSection(lock);
f70863d9 475# endif
71a04cfc
AG
476 OPENSSL_free(lock);
477
478 return;
479}
480
d5e742de
MC
481# define ONCE_UNINITED 0
482# define ONCE_ININIT 1
483# define ONCE_DONE 2
71a04cfc 484
fcb318c6
MC
485/*
486 * We don't use InitOnceExecuteOnce because that isn't available in WinXP which
487 * we still have to support.
488 */
71a04cfc
AG
489int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
490{
491 LONG volatile *lock = (LONG *)once;
492 LONG result;
493
494 if (*lock == ONCE_DONE)
495 return 1;
496
497 do {
498 result = InterlockedCompareExchange(lock, ONCE_ININIT, ONCE_UNINITED);
499 if (result == ONCE_UNINITED) {
349d1cfd 500 init();
1fda5bc4 501 *lock = ONCE_DONE;
71a04cfc
AG
502 return 1;
503 }
504 } while (result == ONCE_ININIT);
505
506 return (*lock == ONCE_DONE);
507}
508
71a04cfc
AG
509int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
510{
511 *key = TlsAlloc();
512 if (*key == TLS_OUT_OF_INDEXES)
513 return 0;
514
515 return 1;
516}
517
518void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
519{
2de108df
DB
520 DWORD last_error;
521 void *ret;
522
523 /*
524 * TlsGetValue clears the last error even on success, so that callers may
525 * distinguish it successfully returning NULL or failing. It is documented
526 * to never fail if the argument is a valid index from TlsAlloc, so we do
527 * not need to handle this.
528 *
529 * However, this error-mangling behavior interferes with the caller's use of
530 * GetLastError. In particular SSL_get_error queries the error queue to
531 * determine whether the caller should look at the OS's errors. To avoid
532 * destroying state, save and restore the Windows error.
533 *
534 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms686812(v=vs.85).aspx
535 */
536 last_error = GetLastError();
537 ret = TlsGetValue(*key);
538 SetLastError(last_error);
539 return ret;
71a04cfc
AG
540}
541
542int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
543{
544 if (TlsSetValue(*key, val) == 0)
545 return 0;
546
547 return 1;
548}
549
550int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
551{
552 if (TlsFree(*key) == 0)
553 return 0;
554
555 return 1;
556}
557
558CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
559{
560 return GetCurrentThreadId();
561}
562
563int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
564{
565 return (a == b);
566}
567
568int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
569{
7da7b27e 570 *ret = (int)InterlockedExchangeAdd((long volatile *)val, (long)amount) + amount;
71a04cfc
AG
571 return 1;
572}
573
d5e742de
MC
574int CRYPTO_atomic_or(uint64_t *val, uint64_t op, uint64_t *ret,
575 CRYPTO_RWLOCK *lock)
576{
2d46a44f
DN
577#if (defined(NO_INTERLOCKEDOR64))
578 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
579 return 0;
580 *val |= op;
581 *ret = *val;
582
583 if (!CRYPTO_THREAD_unlock(lock))
584 return 0;
585
586 return 1;
587#else
d5e742de
MC
588 *ret = (uint64_t)InterlockedOr64((LONG64 volatile *)val, (LONG64)op) | op;
589 return 1;
2d46a44f 590#endif
d5e742de
MC
591}
592
593int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock)
594{
2d46a44f
DN
595#if (defined(NO_INTERLOCKEDOR64))
596 if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
597 return 0;
598 *ret = *val;
599 if (!CRYPTO_THREAD_unlock(lock))
600 return 0;
601
602 return 1;
603#else
d5e742de
MC
604 *ret = (uint64_t)InterlockedOr64((LONG64 volatile *)val, 0);
605 return 1;
2d46a44f 606#endif
d5e742de
MC
607}
608
629b408c
HL
609int CRYPTO_atomic_load_int(int *val, int *ret, CRYPTO_RWLOCK *lock)
610{
611#if (defined(NO_INTERLOCKEDOR64))
612 if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
613 return 0;
614 *ret = *val;
615 if (!CRYPTO_THREAD_unlock(lock))
616 return 0;
617
618 return 1;
619#else
a2c61e41 620 /* On Windows, LONG is always the same size as int. */
629b408c
HL
621 *ret = (int)InterlockedOr((LONG volatile *)val, 0);
622 return 1;
623#endif
624}
625
2915fe19
RS
626int openssl_init_fork_handlers(void)
627{
628 return 0;
629}
630
84952925
DMSP
631int openssl_get_fork_id(void)
632{
633 return 0;
634}
71a04cfc 635#endif