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 15#include "internal.h" 16 17#include <gtest/gtest.h> 18 19#include <openssl/crypto.h> 20#include <openssl/rand.h> 21 22#include "test/test_util.h" 23 24 25#if !defined(OPENSSL_NO_THREADS) 26 27#if defined(OPENSSL_WINDOWS) 28 29OPENSSL_MSVC_PRAGMA(warning(push, 3)) 30#include <windows.h> 31OPENSSL_MSVC_PRAGMA(warning(pop)) 32 33typedef HANDLE thread_t; 34 35static DWORD WINAPI thread_run(LPVOID arg) { 36 void (*thread_func)(void); 37 // VC really doesn't like casting between data and function pointers. 38 OPENSSL_memcpy(&thread_func, &arg, sizeof(thread_func)); 39 thread_func(); 40 return 0; 41} 42 43static int run_thread(thread_t *out_thread, void (*thread_func)(void)) { 44 void *arg; 45 // VC really doesn't like casting between data and function pointers. 46 OPENSSL_memcpy(&arg, &thread_func, sizeof(arg)); 47 48 *out_thread = CreateThread(NULL /* security attributes */, 49 0 /* default stack size */, thread_run, arg, 50 0 /* run immediately */, NULL /* ignore id */); 51 return *out_thread != NULL; 52} 53 54static int wait_for_thread(thread_t thread) { 55 return WaitForSingleObject(thread, INFINITE) == 0; 56} 57 58#else 59 60#include <pthread.h> 61#include <string.h> 62#include <time.h> 63 64typedef pthread_t thread_t; 65 66static void *thread_run(void *arg) { 67 void (*thread_func)(void) = reinterpret_cast<void (*)(void)>(arg); 68 thread_func(); 69 return NULL; 70} 71 72static int run_thread(thread_t *out_thread, void (*thread_func)(void)) { 73 return pthread_create(out_thread, NULL /* default attributes */, thread_run, 74 reinterpret_cast<void *>(thread_func)) == 0; 75} 76 77static int wait_for_thread(thread_t thread) { 78 return pthread_join(thread, NULL) == 0; 79} 80 81#endif // OPENSSL_WINDOWS 82 83static unsigned g_once_init_called = 0; 84 85static void once_init(void) { 86 g_once_init_called++; 87 88 // Sleep briefly so one |call_once_thread| instance will call |CRYPTO_once| 89 // while the other is running this function. 90#if defined(OPENSSL_WINDOWS) 91 Sleep(1 /* milliseconds */); 92#else 93 struct timespec req; 94 OPENSSL_memset(&req, 0, sizeof(req)); 95 req.tv_nsec = 1000000; 96 nanosleep(&req, NULL); 97#endif 98} 99 100static CRYPTO_once_t g_test_once = CRYPTO_ONCE_INIT; 101 102static void call_once_thread(void) { 103 CRYPTO_once(&g_test_once, once_init); 104} 105 106static CRYPTO_once_t once_init_value = CRYPTO_ONCE_INIT; 107static CRYPTO_once_t once_bss; 108 109static struct CRYPTO_STATIC_MUTEX mutex_init_value = CRYPTO_STATIC_MUTEX_INIT; 110static struct CRYPTO_STATIC_MUTEX mutex_bss; 111 112static CRYPTO_EX_DATA_CLASS ex_data_class_value = CRYPTO_EX_DATA_CLASS_INIT; 113static CRYPTO_EX_DATA_CLASS ex_data_class_bss; 114 115TEST(ThreadTest, Once) { 116 ASSERT_EQ(0u, g_once_init_called) 117 << "g_once_init_called was non-zero at start."; 118 119 thread_t thread1, thread2; 120 ASSERT_TRUE(run_thread(&thread1, call_once_thread)); 121 ASSERT_TRUE(run_thread(&thread2, call_once_thread)); 122 ASSERT_TRUE(wait_for_thread(thread1)); 123 ASSERT_TRUE(wait_for_thread(thread2)); 124 125 CRYPTO_once(&g_test_once, once_init); 126 127 EXPECT_EQ(1u, g_once_init_called); 128} 129 130TEST(ThreadTest, InitZeros) { 131 if (FIPS_mode()) { 132 // Our FIPS tooling currently requires that |CRYPTO_ONCE_INIT|, 133 // |CRYPTO_STATIC_MUTEX_INIT| and |CRYPTO_EX_DATA_CLASS| are all zeros and 134 // so can be placed in the BSS section. 135 EXPECT_EQ(Bytes((uint8_t *)&once_bss, sizeof(once_bss)), 136 Bytes((uint8_t *)&once_init_value, sizeof(once_init_value))); 137 EXPECT_EQ(Bytes((uint8_t *)&mutex_bss, sizeof(mutex_bss)), 138 Bytes((uint8_t *)&mutex_init_value, sizeof(mutex_init_value))); 139 EXPECT_EQ( 140 Bytes((uint8_t *)&ex_data_class_bss, sizeof(ex_data_class_bss)), 141 Bytes((uint8_t *)&ex_data_class_value, sizeof(ex_data_class_value))); 142 } 143} 144 145static int g_test_thread_ok = 0; 146static unsigned g_destructor_called_count = 0; 147 148static void thread_local_destructor(void *arg) { 149 if (arg == NULL) { 150 return; 151 } 152 153 unsigned *count = reinterpret_cast<unsigned*>(arg); 154 (*count)++; 155} 156 157TEST(ThreadTest, ThreadLocal) { 158 ASSERT_EQ(nullptr, CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_TEST)) 159 << "Thread-local data was non-NULL at start."; 160 161 thread_t thread; 162 ASSERT_TRUE(run_thread(&thread, []() { 163 if (CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_TEST) != NULL || 164 !CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_TEST, 165 &g_destructor_called_count, 166 thread_local_destructor) || 167 CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_TEST) != 168 &g_destructor_called_count) { 169 return; 170 } 171 172 g_test_thread_ok = 1; 173 })); 174 ASSERT_TRUE(wait_for_thread(thread)); 175 176 EXPECT_TRUE(g_test_thread_ok) << "Thread-local data didn't work in thread."; 177 EXPECT_EQ(1u, g_destructor_called_count); 178 179 // Create a no-op thread to test test that the thread destructor function 180 // works even if thread-local storage wasn't used for a thread. 181 ASSERT_TRUE(run_thread(&thread, []() {})); 182 ASSERT_TRUE(wait_for_thread(thread)); 183} 184 185TEST(ThreadTest, RandState) { 186 // In FIPS mode, rand.c maintains a linked-list of thread-local data because 187 // we're required to clear it on process exit. This test exercises removing a 188 // value from that list. 189 uint8_t buf[1]; 190 RAND_bytes(buf, sizeof(buf)); 191 192 thread_t thread; 193 ASSERT_TRUE(run_thread(&thread, []() { 194 uint8_t buf2[1]; 195 RAND_bytes(buf2, sizeof(buf2)); 196 })); 197 ASSERT_TRUE(wait_for_thread(thread)); 198} 199 200#endif // !OPENSSL_NO_THREADS 201