1 /*
2 * Copyright 2016-2022 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 #include <openssl/crypto.h>
14 #include "internal/cryptlib.h"
15
16 #if defined(__sun)
17 # include <atomic.h>
18 #elif defined(__clang__) && defined(__i386__) && __clang_major__ < 12
19 /* Until https://github.com/llvm/llvm-project/commit/00530dee5d12 */
20 #define __atomic_is_lock_free(size, ptr) ((void)(ptr), size <= sizeof(void *))
21 #endif
22
23 #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS)
24
25 # if defined(OPENSSL_SYS_UNIX)
26 # include <sys/types.h>
27 # include <unistd.h>
28 #endif
29
30 # include <assert.h>
31
32 # ifdef PTHREAD_RWLOCK_INITIALIZER
33 # define USE_RWLOCK
34 # endif
35
CRYPTO_THREAD_lock_new(void)36 CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
37 {
38 # ifdef USE_RWLOCK
39 CRYPTO_RWLOCK *lock;
40
41 if ((lock = OPENSSL_zalloc(sizeof(pthread_rwlock_t))) == NULL) {
42 /* Don't set error, to avoid recursion blowup. */
43 return NULL;
44 }
45
46 if (pthread_rwlock_init(lock, NULL) != 0) {
47 OPENSSL_free(lock);
48 return NULL;
49 }
50 # else
51 pthread_mutexattr_t attr;
52 CRYPTO_RWLOCK *lock;
53
54 if ((lock = OPENSSL_zalloc(sizeof(pthread_mutex_t))) == NULL) {
55 /* Don't set error, to avoid recursion blowup. */
56 return NULL;
57 }
58
59 /*
60 * We don't use recursive mutexes, but try to catch errors if we do.
61 */
62 pthread_mutexattr_init(&attr);
63 # if !defined (__TANDEM) && !defined (_SPT_MODEL_)
64 # if !defined(NDEBUG) && !defined(OPENSSL_NO_MUTEX_ERRORCHECK)
65 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
66 # else
67 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
68 # endif
69 # else
70 /* The SPT Thread Library does not define MUTEX attributes. */
71 # endif
72
73 if (pthread_mutex_init(lock, &attr) != 0) {
74 pthread_mutexattr_destroy(&attr);
75 OPENSSL_free(lock);
76 return NULL;
77 }
78
79 pthread_mutexattr_destroy(&attr);
80 # endif
81
82 return lock;
83 }
84
CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK * lock)85 __owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
86 {
87 # ifdef USE_RWLOCK
88 if (pthread_rwlock_rdlock(lock) != 0)
89 return 0;
90 # else
91 if (pthread_mutex_lock(lock) != 0) {
92 assert(errno != EDEADLK && errno != EBUSY);
93 return 0;
94 }
95 # endif
96
97 return 1;
98 }
99
CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK * lock)100 __owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
101 {
102 # ifdef USE_RWLOCK
103 if (pthread_rwlock_wrlock(lock) != 0)
104 return 0;
105 # else
106 if (pthread_mutex_lock(lock) != 0) {
107 assert(errno != EDEADLK && errno != EBUSY);
108 return 0;
109 }
110 # endif
111
112 return 1;
113 }
114
CRYPTO_THREAD_unlock(CRYPTO_RWLOCK * lock)115 int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
116 {
117 # ifdef USE_RWLOCK
118 if (pthread_rwlock_unlock(lock) != 0)
119 return 0;
120 # else
121 if (pthread_mutex_unlock(lock) != 0) {
122 assert(errno != EPERM);
123 return 0;
124 }
125 # endif
126
127 return 1;
128 }
129
CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK * lock)130 void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
131 {
132 if (lock == NULL)
133 return;
134
135 # ifdef USE_RWLOCK
136 pthread_rwlock_destroy(lock);
137 # else
138 pthread_mutex_destroy(lock);
139 # endif
140 OPENSSL_free(lock);
141
142 return;
143 }
144
CRYPTO_THREAD_run_once(CRYPTO_ONCE * once,void (* init)(void))145 int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
146 {
147 if (pthread_once(once, init) != 0)
148 return 0;
149
150 return 1;
151 }
152
CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL * key,void (* cleanup)(void *))153 int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
154 {
155 if (pthread_key_create(key, cleanup) != 0)
156 return 0;
157
158 return 1;
159 }
160
CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL * key)161 void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
162 {
163 return pthread_getspecific(*key);
164 }
165
CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL * key,void * val)166 int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
167 {
168 if (pthread_setspecific(*key, val) != 0)
169 return 0;
170
171 return 1;
172 }
173
CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL * key)174 int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
175 {
176 if (pthread_key_delete(*key) != 0)
177 return 0;
178
179 return 1;
180 }
181
CRYPTO_THREAD_get_current_id(void)182 CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
183 {
184 return pthread_self();
185 }
186
CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a,CRYPTO_THREAD_ID b)187 int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
188 {
189 return pthread_equal(a, b);
190 }
191
CRYPTO_atomic_add(int * val,int amount,int * ret,CRYPTO_RWLOCK * lock)192 int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
193 {
194 # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL)
195 if (__atomic_is_lock_free(sizeof(*val), val)) {
196 *ret = __atomic_add_fetch(val, amount, __ATOMIC_ACQ_REL);
197 return 1;
198 }
199 # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11))
200 /* This will work for all future Solaris versions. */
201 if (ret != NULL) {
202 *ret = atomic_add_int_nv((volatile unsigned int *)val, amount);
203 return 1;
204 }
205 # endif
206 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
207 return 0;
208
209 *val += amount;
210 *ret = *val;
211
212 if (!CRYPTO_THREAD_unlock(lock))
213 return 0;
214
215 return 1;
216 }
217
CRYPTO_atomic_or(uint64_t * val,uint64_t op,uint64_t * ret,CRYPTO_RWLOCK * lock)218 int CRYPTO_atomic_or(uint64_t *val, uint64_t op, uint64_t *ret,
219 CRYPTO_RWLOCK *lock)
220 {
221 # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL)
222 if (__atomic_is_lock_free(sizeof(*val), val)) {
223 *ret = __atomic_or_fetch(val, op, __ATOMIC_ACQ_REL);
224 return 1;
225 }
226 # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11))
227 /* This will work for all future Solaris versions. */
228 if (ret != NULL) {
229 *ret = atomic_or_64_nv(val, op);
230 return 1;
231 }
232 # endif
233 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
234 return 0;
235 *val |= op;
236 *ret = *val;
237
238 if (!CRYPTO_THREAD_unlock(lock))
239 return 0;
240
241 return 1;
242 }
243
CRYPTO_atomic_load(uint64_t * val,uint64_t * ret,CRYPTO_RWLOCK * lock)244 int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock)
245 {
246 # if defined(__GNUC__) && defined(__ATOMIC_ACQUIRE)
247 if (__atomic_is_lock_free(sizeof(*val), val)) {
248 __atomic_load(val, ret, __ATOMIC_ACQUIRE);
249 return 1;
250 }
251 # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11))
252 /* This will work for all future Solaris versions. */
253 if (ret != NULL) {
254 *ret = atomic_or_64_nv(val, 0);
255 return 1;
256 }
257 # endif
258 if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
259 return 0;
260 *ret = *val;
261 if (!CRYPTO_THREAD_unlock(lock))
262 return 0;
263
264 return 1;
265 }
266 # ifndef FIPS_MODULE
openssl_init_fork_handlers(void)267 int openssl_init_fork_handlers(void)
268 {
269 return 1;
270 }
271 # endif /* FIPS_MODULE */
272
openssl_get_fork_id(void)273 int openssl_get_fork_id(void)
274 {
275 return getpid();
276 }
277 #endif
278