Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2015, Google Inc. |
| 2 | * |
| 3 | * Permission to use, copy, modify, and/or distribute this software for any |
| 4 | * purpose with or without fee is hereby granted, provided that the above |
| 5 | * copyright notice and this permission notice appear in all copies. |
| 6 | * |
| 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 10 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ |
| 14 | |
Bob Beck | fc524c1 | 2023-02-07 14:32:41 -0700 | [diff] [blame] | 15 | // Ensure we can't call OPENSSL_malloc circularly. |
| 16 | #define _BORINGSSL_PROHIBIT_OPENSSL_MALLOC |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 17 | #include "internal.h" |
| 18 | |
David Benjamin | 84759bd | 2016-09-20 17:35:09 -0400 | [diff] [blame] | 19 | #if defined(OPENSSL_WINDOWS_THREADS) |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 20 | |
David Benjamin | a353cdb | 2016-06-09 16:48:33 -0400 | [diff] [blame] | 21 | OPENSSL_MSVC_PRAGMA(warning(push, 3)) |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 22 | #include <windows.h> |
David Benjamin | a353cdb | 2016-06-09 16:48:33 -0400 | [diff] [blame] | 23 | OPENSSL_MSVC_PRAGMA(warning(pop)) |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 24 | |
David Benjamin | b7d6320 | 2022-07-26 13:25:02 -0700 | [diff] [blame] | 25 | #include <assert.h> |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 26 | #include <stdlib.h> |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 27 | #include <string.h> |
| 28 | |
David Benjamin | 9dadc3b | 2016-03-30 19:04:28 -0400 | [diff] [blame] | 29 | static BOOL CALLBACK call_once_init(INIT_ONCE *once, void *arg, void **out) { |
| 30 | void (**init)(void) = (void (**)(void))arg; |
| 31 | (**init)(); |
| 32 | return TRUE; |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 33 | } |
| 34 | |
David Benjamin | 9dadc3b | 2016-03-30 19:04:28 -0400 | [diff] [blame] | 35 | void CRYPTO_once(CRYPTO_once_t *once, void (*init)(void)) { |
| 36 | if (!InitOnceExecuteOnce(once, call_once_init, &init, NULL)) { |
| 37 | abort(); |
| 38 | } |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 39 | } |
| 40 | |
| 41 | void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) { |
David Benjamin | 04c3d40 | 2023-06-03 01:26:29 -0400 | [diff] [blame] | 42 | InitializeSRWLock(lock); |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 43 | } |
| 44 | |
| 45 | void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX *lock) { |
David Benjamin | 04c3d40 | 2023-06-03 01:26:29 -0400 | [diff] [blame] | 46 | AcquireSRWLockShared(lock); |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 47 | } |
| 48 | |
| 49 | void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock) { |
David Benjamin | 04c3d40 | 2023-06-03 01:26:29 -0400 | [diff] [blame] | 50 | AcquireSRWLockExclusive(lock); |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 51 | } |
| 52 | |
David Benjamin | 29270de | 2016-05-24 15:28:36 +0000 | [diff] [blame] | 53 | void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX *lock) { |
David Benjamin | 04c3d40 | 2023-06-03 01:26:29 -0400 | [diff] [blame] | 54 | ReleaseSRWLockShared(lock); |
David Benjamin | 29270de | 2016-05-24 15:28:36 +0000 | [diff] [blame] | 55 | } |
| 56 | |
| 57 | void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX *lock) { |
David Benjamin | 04c3d40 | 2023-06-03 01:26:29 -0400 | [diff] [blame] | 58 | ReleaseSRWLockExclusive(lock); |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 59 | } |
| 60 | |
| 61 | void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 62 | // SRWLOCKs require no cleanup. |
Adam Langley | df1f5e7 | 2015-04-13 11:04:08 -0700 | [diff] [blame] | 63 | } |
| 64 | |
David Benjamin | eb7d5b6 | 2018-05-20 17:44:53 -0400 | [diff] [blame] | 65 | static SRWLOCK g_destructors_lock = SRWLOCK_INIT; |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 66 | static thread_local_destructor_t g_destructors[NUM_OPENSSL_THREAD_LOCALS]; |
| 67 | |
| 68 | static CRYPTO_once_t g_thread_local_init_once = CRYPTO_ONCE_INIT; |
| 69 | static DWORD g_thread_local_key; |
| 70 | static int g_thread_local_failed; |
| 71 | |
| 72 | static void thread_local_init(void) { |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 73 | g_thread_local_key = TlsAlloc(); |
| 74 | g_thread_local_failed = (g_thread_local_key == TLS_OUT_OF_INDEXES); |
| 75 | } |
| 76 | |
David Benjamin | feaa57d | 2016-03-29 14:17:27 -0400 | [diff] [blame] | 77 | static void NTAPI thread_local_destructor(PVOID module, DWORD reason, |
| 78 | PVOID reserved) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 79 | // Only free memory on |DLL_THREAD_DETACH|, not |DLL_PROCESS_DETACH|. In |
| 80 | // VS2015's debug runtime, the C runtime has been unloaded by the time |
| 81 | // |DLL_PROCESS_DETACH| runs. See https://crbug.com/575795. This is consistent |
| 82 | // with |pthread_key_create| which does not call destructors on process exit, |
| 83 | // only thread exit. |
David Benjamin | feaa57d | 2016-03-29 14:17:27 -0400 | [diff] [blame] | 84 | if (reason != DLL_THREAD_DETACH) { |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 85 | return; |
| 86 | } |
| 87 | |
| 88 | CRYPTO_once(&g_thread_local_init_once, thread_local_init); |
| 89 | if (g_thread_local_failed) { |
| 90 | return; |
| 91 | } |
| 92 | |
| 93 | void **pointers = (void**) TlsGetValue(g_thread_local_key); |
| 94 | if (pointers == NULL) { |
| 95 | return; |
| 96 | } |
| 97 | |
| 98 | thread_local_destructor_t destructors[NUM_OPENSSL_THREAD_LOCALS]; |
| 99 | |
David Benjamin | eb7d5b6 | 2018-05-20 17:44:53 -0400 | [diff] [blame] | 100 | AcquireSRWLockExclusive(&g_destructors_lock); |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 101 | OPENSSL_memcpy(destructors, g_destructors, sizeof(destructors)); |
David Benjamin | eb7d5b6 | 2018-05-20 17:44:53 -0400 | [diff] [blame] | 102 | ReleaseSRWLockExclusive(&g_destructors_lock); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 103 | |
David Benjamin | eb7d5b6 | 2018-05-20 17:44:53 -0400 | [diff] [blame] | 104 | for (unsigned i = 0; i < NUM_OPENSSL_THREAD_LOCALS; i++) { |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 105 | if (destructors[i] != NULL) { |
| 106 | destructors[i](pointers[i]); |
| 107 | } |
| 108 | } |
| 109 | |
Bob Beck | fc524c1 | 2023-02-07 14:32:41 -0700 | [diff] [blame] | 110 | free(pointers); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 111 | } |
| 112 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 113 | // Thread Termination Callbacks. |
| 114 | // |
| 115 | // Windows doesn't support a per-thread destructor with its TLS primitives. |
| 116 | // So, we build it manually by inserting a function to be called on each |
| 117 | // thread's exit. This magic is from http://www.codeproject.com/threads/tls.asp |
| 118 | // and it works for VC++ 7.0 and later. |
| 119 | // |
| 120 | // Force a reference to _tls_used to make the linker create the TLS directory |
| 121 | // if it's not already there. (E.g. if __declspec(thread) is not used). Force |
| 122 | // a reference to p_thread_callback_boringssl to prevent whole program |
| 123 | // optimization from discarding the variable. |
David Benjamin | 8c23d3a | 2018-11-25 15:58:02 -0600 | [diff] [blame] | 124 | // |
| 125 | // Note, in the prefixed build, |p_thread_callback_boringssl| may be a macro. |
| 126 | #define STRINGIFY(x) #x |
| 127 | #define EXPAND_AND_STRINGIFY(x) STRINGIFY(x) |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 128 | #ifdef _WIN64 |
David Benjamin | 8c23d3a | 2018-11-25 15:58:02 -0600 | [diff] [blame] | 129 | __pragma(comment(linker, "/INCLUDE:_tls_used")) |
| 130 | __pragma(comment( |
| 131 | linker, "/INCLUDE:" EXPAND_AND_STRINGIFY(p_thread_callback_boringssl))) |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 132 | #else |
David Benjamin | 8c23d3a | 2018-11-25 15:58:02 -0600 | [diff] [blame] | 133 | __pragma(comment(linker, "/INCLUDE:__tls_used")) |
| 134 | __pragma(comment( |
| 135 | linker, "/INCLUDE:_" EXPAND_AND_STRINGIFY(p_thread_callback_boringssl))) |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 136 | #endif |
| 137 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 138 | // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are |
| 139 | // called automatically by the OS loader code (not the CRT) when the module is |
| 140 | // loaded and on thread creation. They are NOT called if the module has been |
| 141 | // loaded by a LoadLibrary() call. It must have implicitly been loaded at |
| 142 | // process startup. |
| 143 | // |
| 144 | // By implicitly loaded, I mean that it is directly referenced by the main EXE |
| 145 | // or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being |
| 146 | // implicitly loaded. |
| 147 | // |
| 148 | // See VC\crt\src\tlssup.c for reference. |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 149 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 150 | // The linker must not discard p_thread_callback_boringssl. (We force a |
| 151 | // reference to this variable with a linker /INCLUDE:symbol pragma to ensure |
| 152 | // that.) If this variable is discarded, the OnThreadExit function will never |
| 153 | // be called. |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 154 | #ifdef _WIN64 |
| 155 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 156 | // .CRT section is merged with .rdata on x64 so it must be constant data. |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 157 | #pragma const_seg(".CRT$XLC") |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 158 | // When defining a const variable, it must have external linkage to be sure the |
| 159 | // linker doesn't discard it. |
David Benjamin | 40acdae | 2015-04-03 11:41:56 -0400 | [diff] [blame] | 160 | extern const PIMAGE_TLS_CALLBACK p_thread_callback_boringssl; |
| 161 | const PIMAGE_TLS_CALLBACK p_thread_callback_boringssl = thread_local_destructor; |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 162 | // Reset the default section. |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 163 | #pragma const_seg() |
| 164 | |
| 165 | #else |
| 166 | |
| 167 | #pragma data_seg(".CRT$XLC") |
David Benjamin | 40acdae | 2015-04-03 11:41:56 -0400 | [diff] [blame] | 168 | PIMAGE_TLS_CALLBACK p_thread_callback_boringssl = thread_local_destructor; |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 169 | // Reset the default section. |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 170 | #pragma data_seg() |
| 171 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 172 | #endif // _WIN64 |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 173 | |
David Benjamin | 3c37d0a | 2018-05-05 00:42:23 -0400 | [diff] [blame] | 174 | static void **get_thread_locals(void) { |
| 175 | // |TlsGetValue| clears the last error even on success, so that callers may |
| 176 | // distinguish it successfully returning NULL or failing. It is documented to |
| 177 | // never fail if the argument is a valid index from |TlsAlloc|, so we do not |
| 178 | // need to handle this. |
| 179 | // |
| 180 | // However, this error-mangling behavior interferes with the caller's use of |
| 181 | // |GetLastError|. In particular |SSL_get_error| queries the error queue to |
| 182 | // determine whether the caller should look at the OS's errors. To avoid |
| 183 | // destroying state, save and restore the Windows error. |
| 184 | // |
| 185 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686812(v=vs.85).aspx |
| 186 | DWORD last_error = GetLastError(); |
| 187 | void **ret = TlsGetValue(g_thread_local_key); |
| 188 | SetLastError(last_error); |
| 189 | return ret; |
| 190 | } |
| 191 | |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 192 | void *CRYPTO_get_thread_local(thread_local_data_t index) { |
| 193 | CRYPTO_once(&g_thread_local_init_once, thread_local_init); |
| 194 | if (g_thread_local_failed) { |
| 195 | return NULL; |
| 196 | } |
| 197 | |
David Benjamin | 3c37d0a | 2018-05-05 00:42:23 -0400 | [diff] [blame] | 198 | void **pointers = get_thread_locals(); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 199 | if (pointers == NULL) { |
| 200 | return NULL; |
| 201 | } |
| 202 | return pointers[index]; |
| 203 | } |
| 204 | |
| 205 | int CRYPTO_set_thread_local(thread_local_data_t index, void *value, |
| 206 | thread_local_destructor_t destructor) { |
| 207 | CRYPTO_once(&g_thread_local_init_once, thread_local_init); |
| 208 | if (g_thread_local_failed) { |
| 209 | destructor(value); |
| 210 | return 0; |
| 211 | } |
| 212 | |
David Benjamin | 3c37d0a | 2018-05-05 00:42:23 -0400 | [diff] [blame] | 213 | void **pointers = get_thread_locals(); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 214 | if (pointers == NULL) { |
Bob Beck | fc524c1 | 2023-02-07 14:32:41 -0700 | [diff] [blame] | 215 | pointers = malloc(sizeof(void *) * NUM_OPENSSL_THREAD_LOCALS); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 216 | if (pointers == NULL) { |
| 217 | destructor(value); |
| 218 | return 0; |
| 219 | } |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 220 | OPENSSL_memset(pointers, 0, sizeof(void *) * NUM_OPENSSL_THREAD_LOCALS); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 221 | if (TlsSetValue(g_thread_local_key, pointers) == 0) { |
Bob Beck | fc524c1 | 2023-02-07 14:32:41 -0700 | [diff] [blame] | 222 | free(pointers); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 223 | destructor(value); |
| 224 | return 0; |
| 225 | } |
| 226 | } |
| 227 | |
David Benjamin | eb7d5b6 | 2018-05-20 17:44:53 -0400 | [diff] [blame] | 228 | AcquireSRWLockExclusive(&g_destructors_lock); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 229 | g_destructors[index] = destructor; |
David Benjamin | eb7d5b6 | 2018-05-20 17:44:53 -0400 | [diff] [blame] | 230 | ReleaseSRWLockExclusive(&g_destructors_lock); |
Adam Langley | d7c5dfb | 2015-03-16 12:48:56 -0700 | [diff] [blame] | 231 | |
| 232 | pointers[index] = value; |
| 233 | return 1; |
| 234 | } |
| 235 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 236 | #endif // OPENSSL_WINDOWS_THREADS |