113d40330Schristos /* 2*8fbed61eSchristos * Copyright 2018-2022 The OpenSSL Project Authors. All Rights Reserved. 313d40330Schristos * 4*8fbed61eSchristos * Licensed under the Apache License 2.0 (the "License"). You may not use 513d40330Schristos * this file except in compliance with the License. You can obtain a copy 613d40330Schristos * in the file LICENSE in the source distribution or at 713d40330Schristos * https://www.openssl.org/source/license.html 813d40330Schristos */ 913d40330Schristos 1013d40330Schristos /* 1113d40330Schristos * Contemporary compilers implement lock-free atomic memory access 1213d40330Schristos * primitives that facilitate writing "thread-opportunistic" or even real 1313d40330Schristos * multi-threading low-overhead code. "Thread-opportunistic" is when 1413d40330Schristos * exact result is not required, e.g. some statistics, or execution flow 1513d40330Schristos * doesn't have to be unambiguous. Simplest example is lazy "constant" 1613d40330Schristos * initialization when one can synchronize on variable itself, e.g. 1713d40330Schristos * 1813d40330Schristos * if (var == NOT_YET_INITIALIZED) 1913d40330Schristos * var = function_returning_same_value(); 2013d40330Schristos * 21403eeac4Schristos * This does work provided that loads and stores are single-instruction 2213d40330Schristos * operations (and integer ones are on *all* supported platforms), but 2313d40330Schristos * it upsets Thread Sanitizer. Suggested solution is 2413d40330Schristos * 2513d40330Schristos * if (tsan_load(&var) == NOT_YET_INITIALIZED) 2613d40330Schristos * tsan_store(&var, function_returning_same_value()); 2713d40330Schristos * 2813d40330Schristos * Production machine code would be the same, so one can wonder why 2913d40330Schristos * bother. Having Thread Sanitizer accept "thread-opportunistic" code 3013d40330Schristos * allows to move on trouble-shooting real bugs. 3113d40330Schristos * 3213d40330Schristos * Resolving Thread Sanitizer nits was the initial purpose for this module, 3313d40330Schristos * but it was later extended with more nuanced primitives that are useful 3413d40330Schristos * even in "non-opportunistic" scenarios. Most notably verifying if a shared 3513d40330Schristos * structure is fully initialized and bypassing the initialization lock. 3613d40330Schristos * It's suggested to view macros defined in this module as "annotations" for 3713d40330Schristos * thread-safe lock-free code, "Thread-Safe ANnotations"... 3813d40330Schristos * 3913d40330Schristos * It's assumed that ATOMIC_{LONG|INT}_LOCK_FREE are assigned same value as 4013d40330Schristos * ATOMIC_POINTER_LOCK_FREE. And check for >= 2 ensures that corresponding 4113d40330Schristos * code is inlined. It should be noted that statistics counters become 4213d40330Schristos * accurate in such case. 4313d40330Schristos * 4413d40330Schristos * Special note about TSAN_QUALIFIER. It might be undesired to use it in 4513d40330Schristos * a shared header. Because whether operation on specific variable or member 4613d40330Schristos * is atomic or not might be irrelevant in other modules. In such case one 4713d40330Schristos * can use TSAN_QUALIFIER in cast specifically when it has to count. 4813d40330Schristos */ 4913d40330Schristos 5013d40330Schristos #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \ 51e0ea3921Schristos && !defined(__STDC_NO_ATOMICS__) && !defined(__lint__) 5213d40330Schristos # include <stdatomic.h> 5313d40330Schristos 5413d40330Schristos # if defined(ATOMIC_POINTER_LOCK_FREE) \ 5513d40330Schristos && ATOMIC_POINTER_LOCK_FREE >= 2 5613d40330Schristos # define TSAN_QUALIFIER _Atomic 5713d40330Schristos # define tsan_load(ptr) atomic_load_explicit((ptr), memory_order_relaxed) 5813d40330Schristos # define tsan_store(ptr, val) atomic_store_explicit((ptr), (val), memory_order_relaxed) 5913d40330Schristos # define tsan_counter(ptr) atomic_fetch_add_explicit((ptr), 1, memory_order_relaxed) 60bf8eace1Schristos # define tsan_decr(ptr) atomic_fetch_add_explicit((ptr), -1, memory_order_relaxed) 6113d40330Schristos # define tsan_ld_acq(ptr) atomic_load_explicit((ptr), memory_order_acquire) 6213d40330Schristos # define tsan_st_rel(ptr, val) atomic_store_explicit((ptr), (val), memory_order_release) 6313d40330Schristos # endif 6413d40330Schristos 6513d40330Schristos #elif defined(__GNUC__) && defined(__ATOMIC_RELAXED) 6613d40330Schristos 6713d40330Schristos # if defined(__GCC_ATOMIC_POINTER_LOCK_FREE) \ 6813d40330Schristos && __GCC_ATOMIC_POINTER_LOCK_FREE >= 2 6913d40330Schristos # define TSAN_QUALIFIER volatile 7013d40330Schristos # define tsan_load(ptr) __atomic_load_n((ptr), __ATOMIC_RELAXED) 7113d40330Schristos # define tsan_store(ptr, val) __atomic_store_n((ptr), (val), __ATOMIC_RELAXED) 7213d40330Schristos # define tsan_counter(ptr) __atomic_fetch_add((ptr), 1, __ATOMIC_RELAXED) 73bf8eace1Schristos # define tsan_decr(ptr) __atomic_fetch_add((ptr), -1, __ATOMIC_RELAXED) 7413d40330Schristos # define tsan_ld_acq(ptr) __atomic_load_n((ptr), __ATOMIC_ACQUIRE) 7513d40330Schristos # define tsan_st_rel(ptr, val) __atomic_store_n((ptr), (val), __ATOMIC_RELEASE) 7613d40330Schristos # endif 7713d40330Schristos 7813d40330Schristos #elif defined(_MSC_VER) && _MSC_VER>=1200 \ 7913d40330Schristos && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64) || \ 804261787cSchristos defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7 && !defined(_WIN32_WCE))) 8113d40330Schristos /* 8213d40330Schristos * There is subtle dependency on /volatile:<iso|ms> command-line option. 8313d40330Schristos * "ms" implies same semantic as memory_order_acquire for loads and 8413d40330Schristos * memory_order_release for stores, while "iso" - memory_order_relaxed for 8513d40330Schristos * either. Real complication is that defaults are different on x86 and ARM. 8613d40330Schristos * There is explanation for that, "ms" is backward compatible with earlier 8713d40330Schristos * compiler versions, while multi-processor ARM can be viewed as brand new 8813d40330Schristos * platform to MSC and its users, and with non-relaxed semantic taking toll 8913d40330Schristos * with additional instructions and penalties, it kind of makes sense to 9013d40330Schristos * default to "iso"... 9113d40330Schristos */ 9213d40330Schristos # define TSAN_QUALIFIER volatile 9313d40330Schristos # if defined(_M_ARM) || defined(_M_ARM64) 9413d40330Schristos # define _InterlockedExchangeAdd _InterlockedExchangeAdd_nf 9513d40330Schristos # pragma intrinsic(_InterlockedExchangeAdd_nf) 9613d40330Schristos # pragma intrinsic(__iso_volatile_load32, __iso_volatile_store32) 9713d40330Schristos # ifdef _WIN64 9813d40330Schristos # define _InterlockedExchangeAdd64 _InterlockedExchangeAdd64_nf 9913d40330Schristos # pragma intrinsic(_InterlockedExchangeAdd64_nf) 10013d40330Schristos # pragma intrinsic(__iso_volatile_load64, __iso_volatile_store64) 10113d40330Schristos # define tsan_load(ptr) (sizeof(*(ptr)) == 8 ? __iso_volatile_load64(ptr) \ 10213d40330Schristos : __iso_volatile_load32(ptr)) 10313d40330Schristos # define tsan_store(ptr, val) (sizeof(*(ptr)) == 8 ? __iso_volatile_store64((ptr), (val)) \ 10413d40330Schristos : __iso_volatile_store32((ptr), (val))) 10513d40330Schristos # else 10613d40330Schristos # define tsan_load(ptr) __iso_volatile_load32(ptr) 10713d40330Schristos # define tsan_store(ptr, val) __iso_volatile_store32((ptr), (val)) 10813d40330Schristos # endif 10913d40330Schristos # else 11013d40330Schristos # define tsan_load(ptr) (*(ptr)) 11113d40330Schristos # define tsan_store(ptr, val) (*(ptr) = (val)) 11213d40330Schristos # endif 11313d40330Schristos # pragma intrinsic(_InterlockedExchangeAdd) 11413d40330Schristos # ifdef _WIN64 11513d40330Schristos # pragma intrinsic(_InterlockedExchangeAdd64) 11613d40330Schristos # define tsan_counter(ptr) (sizeof(*(ptr)) == 8 ? _InterlockedExchangeAdd64((ptr), 1) \ 11713d40330Schristos : _InterlockedExchangeAdd((ptr), 1)) 118bf8eace1Schristos # define tsan_decr(ptr) (sizeof(*(ptr)) == 8 ? _InterlockedExchangeAdd64((ptr), -1) \ 119bf8eace1Schristos : _InterlockedExchangeAdd((ptr), -1)) 12013d40330Schristos # else 12113d40330Schristos # define tsan_counter(ptr) _InterlockedExchangeAdd((ptr), 1) 122bf8eace1Schristos # define tsan_decr(ptr) _InterlockedExchangeAdd((ptr), -1) 12313d40330Schristos # endif 12413d40330Schristos # if !defined(_ISO_VOLATILE) 12513d40330Schristos # define tsan_ld_acq(ptr) (*(ptr)) 12613d40330Schristos # define tsan_st_rel(ptr, val) (*(ptr) = (val)) 12713d40330Schristos # endif 12813d40330Schristos 12913d40330Schristos #endif 13013d40330Schristos 13113d40330Schristos #ifndef TSAN_QUALIFIER 13213d40330Schristos 133*8fbed61eSchristos # ifdef OPENSSL_THREADS 13413d40330Schristos # define TSAN_QUALIFIER volatile 135*8fbed61eSchristos # define TSAN_REQUIRES_LOCKING 136*8fbed61eSchristos # else /* OPENSSL_THREADS */ 137*8fbed61eSchristos # define TSAN_QUALIFIER 138*8fbed61eSchristos # endif /* OPENSSL_THREADS */ 139*8fbed61eSchristos 14013d40330Schristos # define tsan_load(ptr) (*(ptr)) 14113d40330Schristos # define tsan_store(ptr, val) (*(ptr) = (val)) 14213d40330Schristos # define tsan_counter(ptr) ((*(ptr))++) 143bf8eace1Schristos # define tsan_decr(ptr) ((*(ptr))--) 14413d40330Schristos /* 14513d40330Schristos * Lack of tsan_ld_acq and tsan_ld_rel means that compiler support is not 14613d40330Schristos * sophisticated enough to support them. Code that relies on them should be 14713d40330Schristos * protected with #ifdef tsan_ld_acq with locked fallback. 14813d40330Schristos */ 14913d40330Schristos 15013d40330Schristos #endif 151