1 /*
2  * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (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 #include <openssl/crypto.h>
11 #include "internal/cryptlib.h"
12 
13 #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS)
14 
15 # ifdef PTHREAD_RWLOCK_INITIALIZER
16 #  define USE_RWLOCK
17 # endif
18 
19 CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
20 {
21 # ifdef USE_RWLOCK
22     CRYPTO_RWLOCK *lock;
23 
24     if ((lock = OPENSSL_zalloc(sizeof(pthread_rwlock_t))) == NULL) {
25         /* Don't set error, to avoid recursion blowup. */
26         return NULL;
27     }
28 
29     if (pthread_rwlock_init(lock, NULL) != 0) {
30         OPENSSL_free(lock);
31         return NULL;
32     }
33 # else
34     pthread_mutexattr_t attr;
35     CRYPTO_RWLOCK *lock;
36 
37     if ((lock = OPENSSL_zalloc(sizeof(pthread_mutex_t))) == NULL) {
38         /* Don't set error, to avoid recursion blowup. */
39         return NULL;
40     }
41 
42     pthread_mutexattr_init(&attr);
43     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
44 
45     if (pthread_mutex_init(lock, &attr) != 0) {
46         pthread_mutexattr_destroy(&attr);
47         OPENSSL_free(lock);
48         return NULL;
49     }
50 
51     pthread_mutexattr_destroy(&attr);
52 # endif
53 
54     return lock;
55 }
56 
57 int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
58 {
59 # ifdef USE_RWLOCK
60     if (pthread_rwlock_rdlock(lock) != 0)
61         return 0;
62 # else
63     if (pthread_mutex_lock(lock) != 0)
64         return 0;
65 # endif
66 
67     return 1;
68 }
69 
70 int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
71 {
72 # ifdef USE_RWLOCK
73     if (pthread_rwlock_wrlock(lock) != 0)
74         return 0;
75 # else
76     if (pthread_mutex_lock(lock) != 0)
77         return 0;
78 # endif
79 
80     return 1;
81 }
82 
83 int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
84 {
85 # ifdef USE_RWLOCK
86     if (pthread_rwlock_unlock(lock) != 0)
87         return 0;
88 # else
89     if (pthread_mutex_unlock(lock) != 0)
90         return 0;
91 # endif
92 
93     return 1;
94 }
95 
96 void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
97 {
98     if (lock == NULL)
99         return;
100 
101 # ifdef USE_RWLOCK
102     pthread_rwlock_destroy(lock);
103 # else
104     pthread_mutex_destroy(lock);
105 # endif
106     OPENSSL_free(lock);
107 
108     return;
109 }
110 
111 int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
112 {
113     if (pthread_once(once, init) != 0)
114         return 0;
115 
116     return 1;
117 }
118 
119 int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
120 {
121     if (pthread_key_create(key, cleanup) != 0)
122         return 0;
123 
124     return 1;
125 }
126 
127 void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
128 {
129     return pthread_getspecific(*key);
130 }
131 
132 int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
133 {
134     if (pthread_setspecific(*key, val) != 0)
135         return 0;
136 
137     return 1;
138 }
139 
140 int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
141 {
142     if (pthread_key_delete(*key) != 0)
143         return 0;
144 
145     return 1;
146 }
147 
148 CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
149 {
150     return pthread_self();
151 }
152 
153 int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
154 {
155     return pthread_equal(a, b);
156 }
157 
158 int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
159 {
160 # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && \
161      !(defined(__arm__) && __ARM_ARCH < 6)
162     if (__atomic_is_lock_free(sizeof(*val), val)) {
163         *ret = __atomic_add_fetch(val, amount, __ATOMIC_ACQ_REL);
164         return 1;
165     }
166 # endif
167     if (!CRYPTO_THREAD_write_lock(lock))
168         return 0;
169 
170     *val += amount;
171     *ret  = *val;
172 
173     if (!CRYPTO_THREAD_unlock(lock))
174         return 0;
175 
176     return 1;
177 }
178 
179 # ifdef OPENSSL_SYS_UNIX
180 static pthread_once_t fork_once_control = PTHREAD_ONCE_INIT;
181 
182 static void fork_once_func(void)
183 {
184     pthread_atfork(OPENSSL_fork_prepare,
185                    OPENSSL_fork_parent, OPENSSL_fork_child);
186 }
187 # endif
188 
189 int openssl_init_fork_handlers(void)
190 {
191 # ifdef OPENSSL_SYS_UNIX
192     if (pthread_once(&fork_once_control, fork_once_func) == 0)
193         return 1;
194 # endif
195     return 0;
196 }
197 #endif
198