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