1 | /* |
2 | * Copyright 2017-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/ssl/detail/OpenSSLThreading.h> |
18 | |
19 | #include <memory> |
20 | #include <mutex> |
21 | |
22 | #include <folly/Portability.h> |
23 | #include <folly/SharedMutex.h> |
24 | #include <folly/SpinLock.h> |
25 | |
26 | #include <glog/logging.h> |
27 | |
28 | // We cannot directly use portability/openssl because it also depends on us. |
29 | // Therefore we directly use openssl includes. Order of includes is important |
30 | // here. See portability/openssl.h. |
31 | #include <folly/portability/Windows.h> |
32 | #include <openssl/crypto.h> |
33 | |
34 | #if !defined(OPENSSL_IS_BORINGSSL) |
35 | #define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L) |
36 | #else |
37 | #define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (false) |
38 | #endif |
39 | |
40 | // OpenSSL requires us to provide the implementation of CRYPTO_dynlock_value |
41 | // so it must be done in the global namespace. |
42 | struct CRYPTO_dynlock_value { |
43 | std::mutex mutex; |
44 | }; |
45 | |
46 | namespace folly { |
47 | namespace ssl { |
48 | namespace detail { |
49 | |
50 | static std::map<int, LockType>& lockTypes() { |
51 | static auto lockTypesInst = new std::map<int, LockType>(); |
52 | return *lockTypesInst; |
53 | } |
54 | |
55 | void setLockTypes(std::map<int, LockType> inLockTypes) { |
56 | #if FOLLY_SSL_DETAIL_OPENSSL_IS_110 |
57 | LOG(INFO) << "setLockTypes() is unsupported on OpenSSL >= 1.1.0. " |
58 | << "OpenSSL now uses platform native mutexes" ; |
59 | #endif |
60 | |
61 | lockTypes() = inLockTypes; |
62 | } |
63 | |
64 | bool isSSLLockDisabled(int lockId) { |
65 | const auto& sslLocks = lockTypes(); |
66 | const auto it = sslLocks.find(lockId); |
67 | return it != sslLocks.end() && it->second == LockType::NONE; |
68 | } |
69 | |
70 | namespace { |
71 | struct SSLLock { |
72 | explicit SSLLock(LockType inLockType = LockType::MUTEX) |
73 | : lockType(inLockType) {} |
74 | |
75 | void lock(bool read) { |
76 | if (lockType == LockType::MUTEX) { |
77 | mutex.lock(); |
78 | } else if (lockType == LockType::SPINLOCK) { |
79 | spinLock.lock(); |
80 | } else if (lockType == LockType::SHAREDMUTEX) { |
81 | if (read) { |
82 | sharedMutex.lock_shared(); |
83 | } else { |
84 | sharedMutex.lock(); |
85 | } |
86 | } |
87 | // lockType == LOCK_NONE, no-op |
88 | } |
89 | |
90 | void unlock(bool read) { |
91 | if (lockType == LockType::MUTEX) { |
92 | mutex.unlock(); |
93 | } else if (lockType == LockType::SPINLOCK) { |
94 | spinLock.unlock(); |
95 | } else if (lockType == LockType::SHAREDMUTEX) { |
96 | if (read) { |
97 | sharedMutex.unlock_shared(); |
98 | } else { |
99 | sharedMutex.unlock(); |
100 | } |
101 | } |
102 | // lockType == LOCK_NONE, no-op |
103 | } |
104 | |
105 | LockType lockType; |
106 | folly::SpinLock spinLock{}; |
107 | std::mutex mutex; |
108 | SharedMutex sharedMutex; |
109 | }; |
110 | } // namespace |
111 | |
112 | // Statics are unsafe in environments that call exit(). |
113 | // If one thread calls exit() while another thread is |
114 | // references a member of SSLContext, bad things can happen. |
115 | // SSLContext runs in such environments. |
116 | // Instead of declaring a static member we "new" the static |
117 | // member so that it won't be destructed on exit(). |
118 | static std::unique_ptr<SSLLock[]>& locks() { |
119 | static auto locksInst = new std::unique_ptr<SSLLock[]>(); |
120 | return *locksInst; |
121 | } |
122 | |
123 | static void callbackLocking(int mode, int n, const char*, int) { |
124 | if (mode & CRYPTO_LOCK) { |
125 | locks()[size_t(n)].lock(mode & CRYPTO_READ); |
126 | } else { |
127 | locks()[size_t(n)].unlock(mode & CRYPTO_READ); |
128 | } |
129 | } |
130 | |
131 | static unsigned long callbackThreadID() { |
132 | return static_cast<unsigned long>(folly::getCurrentThreadID()); |
133 | } |
134 | |
135 | static CRYPTO_dynlock_value* dyn_create(const char*, int) { |
136 | return new CRYPTO_dynlock_value; |
137 | } |
138 | |
139 | static void |
140 | dyn_lock(int mode, struct CRYPTO_dynlock_value* lock, const char*, int) { |
141 | if (lock != nullptr) { |
142 | if (mode & CRYPTO_LOCK) { |
143 | lock->mutex.lock(); |
144 | } else { |
145 | lock->mutex.unlock(); |
146 | } |
147 | } |
148 | } |
149 | |
150 | static void dyn_destroy(struct CRYPTO_dynlock_value* lock, const char*, int) { |
151 | delete lock; |
152 | } |
153 | |
154 | void installThreadingLocks() { |
155 | // static locking |
156 | locks() = std::make_unique<SSLLock[]>(size_t(CRYPTO_num_locks())); |
157 | for (auto it : lockTypes()) { |
158 | locks()[size_t(it.first)].lockType = it.second; |
159 | } |
160 | CRYPTO_set_id_callback(callbackThreadID); |
161 | CRYPTO_set_locking_callback(callbackLocking); |
162 | // dynamic locking |
163 | CRYPTO_set_dynlock_create_callback(dyn_create); |
164 | CRYPTO_set_dynlock_lock_callback(dyn_lock); |
165 | CRYPTO_set_dynlock_destroy_callback(dyn_destroy); |
166 | } |
167 | |
168 | void cleanupThreadingLocks() { |
169 | CRYPTO_set_id_callback(nullptr); |
170 | CRYPTO_set_locking_callback(nullptr); |
171 | CRYPTO_set_dynlock_create_callback(nullptr); |
172 | CRYPTO_set_dynlock_lock_callback(nullptr); |
173 | CRYPTO_set_dynlock_destroy_callback(nullptr); |
174 | locks().reset(); |
175 | } |
176 | |
177 | } // namespace detail |
178 | } // namespace ssl |
179 | } // namespace folly |
180 | |