mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-10-28 10:45:48 +08:00
libtsan: update to LLVM 23
This commit is contained in:
55 files changed
+1399
-297
No files matched your search
+10
@@ -362,6 +362,16 @@ const interpose_substitution substitution_##func_name[] \
|
||||
// so we use casts via uintptr_t (the local __sanitizer::uptr equivalent).
|
||||
namespace __interception {
|
||||
|
||||
// Dynamic library loading helpers (dlopen/LoadLibrary, dlsym/GetProcAddress).
|
||||
// Implemented in interception_linux.cpp on non-Windows targets and
|
||||
// interception_win.cpp on Windows.
|
||||
bool DynamicLoaderAvailable();
|
||||
void* OpenLibrary(const char* name);
|
||||
void* LookupSymbol(void* handle, const char* symbol);
|
||||
void* LookupSymbolDefault(const char* symbol);
|
||||
void* LookupSymbolNext(const char* symbol);
|
||||
void* LookupSymbolNextVersioned(const char* symbol, const char* version);
|
||||
|
||||
#if defined(__ELF__) && !SANITIZER_FUCHSIA
|
||||
// The use of interceptors makes many sanitizers unusable for static linking.
|
||||
// Define a function, if called, will cause a linker error (undefined _DYNAMIC).
|
||||
|
||||
+58
-5
@@ -9,15 +9,68 @@
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Linux-specific interception methods.
|
||||
//
|
||||
// POSIX dynamic-library helpers (dlopen / dlsym) live here for every
|
||||
// non-Windows interception target (Linux, *BSD, Darwin, AIX, Fuchsia, ...).
|
||||
// macOS/AIX/Fuchsia compile this TU for RTInterception but do not use the
|
||||
// Linux-specific InterceptFunction helpers below.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "interception.h"
|
||||
|
||||
#if !SANITIZER_WINDOWS
|
||||
|
||||
# include <dlfcn.h>
|
||||
|
||||
# pragma weak dlopen
|
||||
# pragma weak dlsym
|
||||
#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
# pragma weak dlvsym
|
||||
#endif
|
||||
|
||||
namespace __interception {
|
||||
|
||||
bool DynamicLoaderAvailable() { return dlopen != nullptr && dlsym != nullptr; }
|
||||
|
||||
void* OpenLibrary(const char* name) {
|
||||
if (!DynamicLoaderAvailable())
|
||||
return nullptr;
|
||||
return dlopen(name, RTLD_LAZY | RTLD_LOCAL);
|
||||
}
|
||||
|
||||
void* LookupSymbol(void* handle, const char* symbol) {
|
||||
if (!DynamicLoaderAvailable())
|
||||
return nullptr;
|
||||
return dlsym(handle, symbol);
|
||||
}
|
||||
|
||||
void* LookupSymbolDefault(const char* symbol) {
|
||||
if (!DynamicLoaderAvailable())
|
||||
return nullptr;
|
||||
return dlsym(RTLD_DEFAULT, symbol);
|
||||
}
|
||||
|
||||
void* LookupSymbolNext(const char* symbol) {
|
||||
if (!DynamicLoaderAvailable())
|
||||
return nullptr;
|
||||
return dlsym(RTLD_NEXT, symbol);
|
||||
}
|
||||
|
||||
#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
void* LookupSymbolNextVersioned(const char* symbol, const char* version) {
|
||||
if (!DynamicLoaderAvailable() || dlvsym == nullptr)
|
||||
return nullptr;
|
||||
return dlvsym(RTLD_NEXT, symbol, version);
|
||||
}
|
||||
#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
|
||||
} // namespace __interception
|
||||
|
||||
#endif // !SANITIZER_WINDOWS
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
|
||||
SANITIZER_SOLARIS || SANITIZER_HAIKU
|
||||
|
||||
#include <dlfcn.h> // for dlsym() and dlvsym()
|
||||
|
||||
namespace __interception {
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
@@ -39,14 +92,14 @@ static void *GetFuncAddr(const char *name, uptr trampoline) {
|
||||
if (StrCmp(name, "sigaction"))
|
||||
name = "__sigaction14";
|
||||
#endif
|
||||
void *addr = dlsym(RTLD_NEXT, name);
|
||||
void* addr = LookupSymbolNext(name);
|
||||
if (!addr) {
|
||||
// If the lookup using RTLD_NEXT failed, the sanitizer runtime library is
|
||||
// later in the library search order than the DSO that we are trying to
|
||||
// intercept, which means that we cannot intercept this function. We still
|
||||
// want the address of the real definition, though, so look it up using
|
||||
// RTLD_DEFAULT.
|
||||
addr = dlsym(RTLD_DEFAULT, name);
|
||||
addr = LookupSymbolDefault(name);
|
||||
|
||||
// In case `name' is not loaded, dlsym ends up finding the actual wrapper.
|
||||
// We don't want to intercept the wrapper and have it point to itself.
|
||||
@@ -66,7 +119,7 @@ bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,
|
||||
// dlvsym is a GNU extension supported by some other platforms.
|
||||
#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
static void *GetFuncAddr(const char *name, const char *ver) {
|
||||
return dlvsym(RTLD_NEXT, name, ver);
|
||||
return LookupSymbolNextVersioned(name, ver);
|
||||
}
|
||||
|
||||
bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
|
||||
|
||||
+25
@@ -134,6 +134,30 @@
|
||||
|
||||
namespace __interception {
|
||||
|
||||
bool DynamicLoaderAvailable() { return true; }
|
||||
|
||||
void* OpenLibrary(const char* name) {
|
||||
if (!name)
|
||||
return reinterpret_cast<void*>(GetModuleHandleA(nullptr));
|
||||
return reinterpret_cast<void*>(LoadLibraryA(name));
|
||||
}
|
||||
|
||||
void* LookupSymbol(void* handle, const char* symbol) {
|
||||
if (!handle)
|
||||
return nullptr;
|
||||
return reinterpret_cast<void*>(reinterpret_cast<__sanitizer::uptr>(
|
||||
GetProcAddress(reinterpret_cast<HMODULE>(handle), symbol)));
|
||||
}
|
||||
|
||||
void* LookupSymbolDefault(const char* symbol) {
|
||||
return LookupSymbol(reinterpret_cast<void*>(GetModuleHandleA(nullptr)),
|
||||
symbol);
|
||||
}
|
||||
|
||||
void* LookupSymbolNext(const char*) { return nullptr; }
|
||||
|
||||
void* LookupSymbolNextVersioned(const char*, const char*) { return nullptr; }
|
||||
|
||||
static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
|
||||
static const int kJumpInstructionLength = 5;
|
||||
static const int kShortJumpInstructionLength = 2;
|
||||
@@ -655,6 +679,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
return 2;
|
||||
|
||||
case 0x3980: // 80 39 XX : cmp BYTE PTR [rcx], XX
|
||||
case 0x3a80: // 80 3A XX : cmp BYTE PTR [rdx], XX
|
||||
case 0x4D8B: // 8B 4D XX : mov XX(%ebp), ecx
|
||||
case 0x558B: // 8B 55 XX : mov XX(%ebp), edx
|
||||
case 0x758B: // 8B 75 XX : mov XX(%ebp), esp
|
||||
|
||||
+3
-2
@@ -109,14 +109,15 @@ void *InternalReallocArray(void *addr, uptr count, uptr size,
|
||||
return InternalRealloc(addr, count * size, cache);
|
||||
}
|
||||
|
||||
void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) {
|
||||
void* InternalCalloc(uptr count, uptr size, InternalAllocatorCache* cache,
|
||||
uptr alignment) {
|
||||
if (UNLIKELY(CheckForCallocOverflow(count, size))) {
|
||||
Report("FATAL: %s: calloc parameters overflow: count * size (%zd * %zd) "
|
||||
"cannot be represented in type size_t\n", SanitizerToolName, count,
|
||||
size);
|
||||
Die();
|
||||
}
|
||||
void *p = InternalAlloc(count * size, cache);
|
||||
void* p = InternalAlloc(count * size, cache, alignment);
|
||||
if (LIKELY(p))
|
||||
internal_memset(p, 0, count * size);
|
||||
return p;
|
||||
|
||||
@@ -47,12 +47,6 @@ void PrintHintAllocatorCannotReturnNull();
|
||||
// Callback type for iterating over chunks.
|
||||
typedef void (*ForEachChunkCallback)(uptr chunk, void *arg);
|
||||
|
||||
inline u32 Rand(u32 *state) { // ANSI C linear congruential PRNG.
|
||||
return (*state = *state * 1103515245 + 12345) >> 16;
|
||||
}
|
||||
|
||||
inline u32 RandN(u32 *state, u32 n) { return Rand(state) % n; } // [0, n)
|
||||
|
||||
template<typename T>
|
||||
inline void RandomShuffle(T *a, u32 n, u32 *rand_state) {
|
||||
if (n <= 1) return;
|
||||
|
||||
@@ -40,8 +40,8 @@ struct DlSymAllocator {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *Callocate(usize nmemb, usize size) {
|
||||
void *ptr = InternalCalloc(nmemb, size);
|
||||
static void* Callocate(usize nmemb, usize size, uptr align = kWordSize) {
|
||||
void* ptr = InternalCalloc(nmemb, size, nullptr, align);
|
||||
CHECK(internal_allocator()->FromPrimary(ptr));
|
||||
Details::OnAllocate(ptr, GetSize(ptr));
|
||||
return ptr;
|
||||
@@ -53,9 +53,9 @@ struct DlSymAllocator {
|
||||
InternalFree(ptr);
|
||||
}
|
||||
|
||||
static void *Realloc(void *ptr, uptr new_size) {
|
||||
static void* Realloc(void* ptr, uptr new_size, uptr align = kWordSize) {
|
||||
if (!ptr)
|
||||
return Allocate(new_size);
|
||||
return Allocate(new_size, align);
|
||||
CHECK(internal_allocator()->FromPrimary(ptr));
|
||||
if (!new_size) {
|
||||
Free(ptr);
|
||||
@@ -63,7 +63,7 @@ struct DlSymAllocator {
|
||||
}
|
||||
uptr size = GetSize(ptr);
|
||||
uptr memcpy_size = Min(new_size, size);
|
||||
void *new_ptr = Allocate(new_size);
|
||||
void* new_ptr = Allocate(new_size, align);
|
||||
if (new_ptr)
|
||||
internal_memcpy(new_ptr, ptr, memcpy_size);
|
||||
Free(ptr);
|
||||
|
||||
@@ -45,8 +45,9 @@ void *InternalRealloc(void *p, uptr size,
|
||||
InternalAllocatorCache *cache = nullptr);
|
||||
void *InternalReallocArray(void *p, uptr count, uptr size,
|
||||
InternalAllocatorCache *cache = nullptr);
|
||||
void *InternalCalloc(uptr count, uptr size,
|
||||
InternalAllocatorCache *cache = nullptr);
|
||||
void* InternalCalloc(uptr count, uptr size,
|
||||
InternalAllocatorCache* cache = nullptr,
|
||||
uptr alignment = 0);
|
||||
void InternalFree(void *p, InternalAllocatorCache *cache = nullptr);
|
||||
void InternalAllocatorLock();
|
||||
void InternalAllocatorUnlock();
|
||||
|
||||
+4
-2
@@ -61,6 +61,8 @@
|
||||
# define ASM_TAIL_CALL jg
|
||||
#elif defined(__riscv)
|
||||
# define ASM_TAIL_CALL tail
|
||||
#elif defined(__hexagon__)
|
||||
# define ASM_TAIL_CALL jump
|
||||
#endif
|
||||
|
||||
// Currently, almost all of the shared libraries rely on the value of
|
||||
@@ -103,8 +105,8 @@
|
||||
# define ASM_SIZE(symbol) .size symbol, .-symbol
|
||||
# define ASM_SYMBOL(symbol) symbol
|
||||
# define ASM_SYMBOL_INTERCEPTOR(symbol) symbol
|
||||
# if defined(__i386__) || defined(__powerpc__) || defined(__s390__) || \
|
||||
defined(__sparc__)
|
||||
# if defined(__i386__) || defined(__powerpc__) || defined(__s390__) || \
|
||||
defined(__sparc__) || defined(__alpha__)
|
||||
// For details, see interception.h
|
||||
# define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
|
||||
# define ASM_TRAMPOLINE_ALIAS(symbol, name) \
|
||||
|
||||
+19
-4
@@ -387,8 +387,8 @@ void ReportDeadlySignal(const SignalContext &sig, u32 tid,
|
||||
const void *unwind_context);
|
||||
|
||||
// Alternative signal stack (POSIX-only).
|
||||
void SetAlternateSignalStack();
|
||||
void UnsetAlternateSignalStack();
|
||||
void* SetAlternateSignalStack();
|
||||
void UnsetAlternateSignalStack(void* altstack_base);
|
||||
|
||||
bool IsSignalHandlerFromSanitizer(int signum);
|
||||
bool SetSignalHandlerFromSanitizer(int signum, bool new_state);
|
||||
@@ -906,7 +906,14 @@ class LoadedModule {
|
||||
class ListOfModules {
|
||||
public:
|
||||
ListOfModules() : initialized(false) {}
|
||||
~ListOfModules() { clear(); }
|
||||
~ListOfModules() {
|
||||
clear();
|
||||
if (initialized)
|
||||
modules_.Destroy();
|
||||
}
|
||||
ListOfModules(const ListOfModules&) = delete;
|
||||
ListOfModules& operator=(const ListOfModules&) = delete;
|
||||
|
||||
void init();
|
||||
void fallbackInit(); // Uses fallback init if available, otherwise clears
|
||||
const LoadedModule *begin() const { return modules_.begin(); }
|
||||
@@ -1085,7 +1092,9 @@ struct StackDepotStats {
|
||||
// indicate that sanitizer allocator should not attempt to release memory to OS.
|
||||
const s32 kReleaseToOSIntervalNever = -1;
|
||||
|
||||
void CheckNoDeepBind(const char *filename, int flag);
|
||||
// Platform hook invoked before dlopen. Performs platform-specific dlopen flag
|
||||
// checks (e.g. RTLD_DEEPBIND on Linux).
|
||||
void OnDlOpen(const char* filename, int flag);
|
||||
|
||||
// Returns the requested amount of random data (up to 256 bytes) that can then
|
||||
// be used to seed a PRNG. Defaults to blocking like the underlying syscall.
|
||||
@@ -1100,6 +1109,12 @@ inline u32 GetNumberOfCPUsCached() {
|
||||
return NumberOfCPUsCached;
|
||||
}
|
||||
|
||||
inline u32 Rand(u32* state) { // ANSI C linear congruential PRNG.
|
||||
return (*state = *state * 1103515245 + 12345) >> 16;
|
||||
}
|
||||
|
||||
inline u32 RandN(u32* state, u32 n) { return Rand(state) % n; } // [0, n)
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
inline void *operator new(__sanitizer::usize size,
|
||||
|
||||
@@ -277,8 +277,11 @@ extern const short *_tolower_tab_;
|
||||
common_flags()->strict_string_checks ? (internal_strlen(s)) + 1 : (n) )
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_DLOPEN
|
||||
#define COMMON_INTERCEPTOR_DLOPEN(filename, flag) \
|
||||
({ CheckNoDeepBind(filename, flag); REAL(dlopen)(filename, flag); })
|
||||
# define COMMON_INTERCEPTOR_DLOPEN(filename, flag) \
|
||||
({ \
|
||||
OnDlOpen(filename, flag); \
|
||||
REAL(dlopen)(filename, flag); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE
|
||||
@@ -1023,6 +1026,25 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
|
||||
#define INIT_READ
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT___READ_CHK
|
||||
INTERCEPTOR(SSIZE_T, __read_chk, int fd, void* ptr, SIZE_T count,
|
||||
SIZE_T buflen) {
|
||||
void* ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __read_chk, fd, ptr, count, buflen);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res =
|
||||
COMMON_INTERCEPTOR_BLOCK_REAL(__read_chk)(fd, ptr, count, buflen);
|
||||
if (res > 0)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
|
||||
if (res >= 0 && fd >= 0)
|
||||
COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
}
|
||||
# define INIT___READ_CHK COMMON_INTERCEPT_FUNCTION(__read_chk)
|
||||
#else
|
||||
# define INIT___READ_CHK
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_FREAD
|
||||
INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) {
|
||||
// libc file streams can call user-supplied functions, see fopencookie.
|
||||
@@ -1058,6 +1080,25 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
|
||||
#define INIT_PREAD
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT___PREAD_CHK
|
||||
INTERCEPTOR(SSIZE_T, __pread_chk, int fd, void* ptr, SIZE_T count, OFF_T offset,
|
||||
SIZE_T buflen) {
|
||||
void* ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __pread_chk, fd, ptr, count, offset, buflen);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(__pread_chk)(fd, ptr, count,
|
||||
offset, buflen);
|
||||
if (res > 0)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
|
||||
if (res >= 0 && fd >= 0)
|
||||
COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
}
|
||||
# define INIT___PREAD_CHK COMMON_INTERCEPT_FUNCTION(__pread_chk)
|
||||
#else
|
||||
# define INIT___PREAD_CHK
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_PREAD64
|
||||
INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
|
||||
void *ctx;
|
||||
@@ -1076,6 +1117,25 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
|
||||
#define INIT_PREAD64
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT___PREAD64_CHK
|
||||
INTERCEPTOR(SSIZE_T, __pread64_chk, int fd, void* ptr, SIZE_T count,
|
||||
OFF64_T offset, SIZE_T buflen) {
|
||||
void* ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __pread64_chk, fd, ptr, count, offset, buflen);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(__pread64_chk)(fd, ptr, count,
|
||||
offset, buflen);
|
||||
if (res > 0)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
|
||||
if (res >= 0 && fd >= 0)
|
||||
COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
}
|
||||
# define INIT___PREAD64_CHK COMMON_INTERCEPT_FUNCTION(__pread64_chk)
|
||||
#else
|
||||
# define INIT___PREAD64_CHK
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_READV
|
||||
INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov,
|
||||
int iovcnt) {
|
||||
@@ -10428,9 +10488,12 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_MEMRCHR;
|
||||
INIT_MEMMEM;
|
||||
INIT_READ;
|
||||
INIT___READ_CHK;
|
||||
INIT_FREAD;
|
||||
INIT_PREAD;
|
||||
INIT___PREAD_CHK;
|
||||
INIT_PREAD64;
|
||||
INIT___PREAD64_CHK;
|
||||
INIT_READV;
|
||||
INIT_PREADV;
|
||||
INIT_PREADV64;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
|
||||
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
|
||||
INTERFACE_FUNCTION(__sanitizer_annotate_double_ended_contiguous_container)
|
||||
INTERFACE_FUNCTION(__sanitizer_copy_contiguous_container_annotations)
|
||||
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
|
||||
INTERFACE_FUNCTION(
|
||||
__sanitizer_double_ended_contiguous_container_find_bad_address)
|
||||
|
||||
+57
-95
@@ -44,10 +44,10 @@ class DenseMapBase {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (getNumEntries() == 0 && getNumTombstones() == 0)
|
||||
if (getNumEntries() == 0)
|
||||
return;
|
||||
|
||||
const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
if (__sanitizer::is_trivially_destructible<ValueT>::value) {
|
||||
// Use a simpler loop when values don't need destruction.
|
||||
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P)
|
||||
@@ -56,17 +56,14 @@ class DenseMapBase {
|
||||
unsigned NumEntries = getNumEntries();
|
||||
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
|
||||
if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) {
|
||||
if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) {
|
||||
P->getSecond().~ValueT();
|
||||
--NumEntries;
|
||||
}
|
||||
P->getSecond().~ValueT();
|
||||
--NumEntries;
|
||||
P->getFirst() = EmptyKey;
|
||||
}
|
||||
}
|
||||
CHECK_EQ(NumEntries, 0);
|
||||
}
|
||||
setNumEntries(0);
|
||||
setNumTombstones(0);
|
||||
}
|
||||
|
||||
/// Return true if the specified key is in the map, false otherwise.
|
||||
@@ -171,20 +168,13 @@ class DenseMapBase {
|
||||
if (!TheBucket)
|
||||
return false; // not in map.
|
||||
|
||||
TheBucket->getSecond().~ValueT();
|
||||
TheBucket->getFirst() = getTombstoneKey();
|
||||
decrementNumEntries();
|
||||
incrementNumTombstones();
|
||||
eraseFromFilledBucket(TheBucket);
|
||||
return true;
|
||||
}
|
||||
|
||||
void erase(value_type *I) {
|
||||
CHECK_NE(I, nullptr);
|
||||
BucketT *TheBucket = &*I;
|
||||
TheBucket->getSecond().~ValueT();
|
||||
TheBucket->getFirst() = getTombstoneKey();
|
||||
decrementNumEntries();
|
||||
incrementNumTombstones();
|
||||
eraseFromFilledBucket(I);
|
||||
}
|
||||
|
||||
value_type &FindAndConstruct(const KeyT &Key) {
|
||||
@@ -214,11 +204,10 @@ class DenseMapBase {
|
||||
/// Function can return fast to stop the process.
|
||||
template <class Fn>
|
||||
void forEach(Fn fn) {
|
||||
const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
for (auto *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
|
||||
const KeyT K = P->getFirst();
|
||||
if (!KeyInfoT::isEqual(K, EmptyKey) &&
|
||||
!KeyInfoT::isEqual(K, TombstoneKey)) {
|
||||
if (!KeyInfoT::isEqual(K, EmptyKey)) {
|
||||
if (!fn(*P))
|
||||
return;
|
||||
}
|
||||
@@ -238,10 +227,9 @@ class DenseMapBase {
|
||||
if (getNumBuckets() == 0) // Nothing to do.
|
||||
return;
|
||||
|
||||
const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
|
||||
if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey) &&
|
||||
!KeyInfoT::isEqual(P->getFirst(), TombstoneKey))
|
||||
if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey))
|
||||
P->getSecond().~ValueT();
|
||||
P->getFirst().~KeyT();
|
||||
}
|
||||
@@ -249,7 +237,6 @@ class DenseMapBase {
|
||||
|
||||
void initEmpty() {
|
||||
setNumEntries(0);
|
||||
setNumTombstones(0);
|
||||
|
||||
CHECK_EQ((getNumBuckets() & (getNumBuckets() - 1)), 0);
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
@@ -273,10 +260,8 @@ class DenseMapBase {
|
||||
|
||||
// Insert all the old elements.
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
const KeyT TombstoneKey = getTombstoneKey();
|
||||
for (BucketT *B = OldBucketsBegin, *E = OldBucketsEnd; B != E; ++B) {
|
||||
if (!KeyInfoT::isEqual(B->getFirst(), EmptyKey) &&
|
||||
!KeyInfoT::isEqual(B->getFirst(), TombstoneKey)) {
|
||||
if (!KeyInfoT::isEqual(B->getFirst(), EmptyKey)) {
|
||||
// Insert the key/value into the new table.
|
||||
BucketT *DestBucket;
|
||||
bool FoundVal = LookupBucketFor(B->getFirst(), DestBucket);
|
||||
@@ -301,7 +286,6 @@ class DenseMapBase {
|
||||
CHECK_EQ(getNumBuckets(), other.getNumBuckets());
|
||||
|
||||
setNumEntries(other.getNumEntries());
|
||||
setNumTombstones(other.getNumTombstones());
|
||||
|
||||
if (__sanitizer::is_trivially_copyable<KeyT>::value &&
|
||||
__sanitizer::is_trivially_copyable<ValueT>::value)
|
||||
@@ -311,8 +295,7 @@ class DenseMapBase {
|
||||
for (uptr i = 0; i < getNumBuckets(); ++i) {
|
||||
::new (&getBuckets()[i].getFirst())
|
||||
KeyT(other.getBuckets()[i].getFirst());
|
||||
if (!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getEmptyKey()) &&
|
||||
!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getTombstoneKey()))
|
||||
if (!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getEmptyKey()))
|
||||
::new (&getBuckets()[i].getSecond())
|
||||
ValueT(other.getBuckets()[i].getSecond());
|
||||
}
|
||||
@@ -329,9 +312,40 @@ class DenseMapBase {
|
||||
|
||||
static const KeyT getEmptyKey() { return KeyInfoT::getEmptyKey(); }
|
||||
|
||||
static const KeyT getTombstoneKey() { return KeyInfoT::getTombstoneKey(); }
|
||||
|
||||
private:
|
||||
/// Erase the entry at \p TheBucket and close the resulting hole via Knuth
|
||||
/// TAOCP 6.4 Algorithm R: walk forward over the cluster, shifting back any
|
||||
/// entry whose linear-probe chain from its home bucket passes through the
|
||||
/// hole, until an empty bucket terminates the cluster.
|
||||
void eraseFromFilledBucket(BucketT* TheBucket) {
|
||||
TheBucket->getSecond().~ValueT();
|
||||
decrementNumEntries();
|
||||
|
||||
BucketT* BucketsPtr = getBuckets();
|
||||
const unsigned NumBuckets = getNumBuckets();
|
||||
const unsigned Mask = NumBuckets - 1;
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
unsigned I = static_cast<unsigned>(TheBucket - BucketsPtr);
|
||||
unsigned J = I;
|
||||
while (true) {
|
||||
J = (J + 1) & Mask;
|
||||
BucketT& BJ = BucketsPtr[J];
|
||||
if (KeyInfoT::isEqual(BJ.getFirst(), EmptyKey))
|
||||
break;
|
||||
unsigned Ideal = getHashValue(BJ.getFirst()) & Mask;
|
||||
// If the hole (I) lies on the linear-probe chain from the home bucket
|
||||
// (Ideal) to J, shift J into the hole and make J the new hole.
|
||||
if (((I - Ideal) & Mask) < ((J - Ideal) & Mask)) {
|
||||
BucketT& BI = BucketsPtr[I];
|
||||
BI.getFirst() = __sanitizer::move(BJ.getFirst());
|
||||
::new (&BI.getSecond()) ValueT(__sanitizer::move(BJ.getSecond()));
|
||||
BJ.getSecond().~ValueT();
|
||||
I = J;
|
||||
}
|
||||
}
|
||||
BucketsPtr[I].getFirst() = EmptyKey;
|
||||
}
|
||||
|
||||
unsigned getNumEntries() const {
|
||||
return static_cast<const DerivedT *>(this)->getNumEntries();
|
||||
}
|
||||
@@ -344,18 +358,6 @@ class DenseMapBase {
|
||||
|
||||
void decrementNumEntries() { setNumEntries(getNumEntries() - 1); }
|
||||
|
||||
unsigned getNumTombstones() const {
|
||||
return static_cast<const DerivedT *>(this)->getNumTombstones();
|
||||
}
|
||||
|
||||
void setNumTombstones(unsigned Num) {
|
||||
static_cast<DerivedT *>(this)->setNumTombstones(Num);
|
||||
}
|
||||
|
||||
void incrementNumTombstones() { setNumTombstones(getNumTombstones() + 1); }
|
||||
|
||||
void decrementNumTombstones() { setNumTombstones(getNumTombstones() - 1); }
|
||||
|
||||
const BucketT *getBuckets() const {
|
||||
return static_cast<const DerivedT *>(this)->getBuckets();
|
||||
}
|
||||
@@ -398,25 +400,16 @@ class DenseMapBase {
|
||||
template <typename LookupKeyT>
|
||||
BucketT *InsertIntoBucketImpl(const KeyT &Key, const LookupKeyT &Lookup,
|
||||
BucketT *TheBucket) {
|
||||
// If the load of the hash table is more than 3/4, or if fewer than 1/8 of
|
||||
// the buckets are empty (meaning that many are filled with tombstones),
|
||||
// grow the table.
|
||||
//
|
||||
// The later case is tricky. For example, if we had one empty bucket with
|
||||
// tons of tombstones, failing lookups (e.g. for insertion) would have to
|
||||
// probe almost the entire table until it found the empty bucket. If the
|
||||
// table completely filled with tombstones, no lookup would ever succeed,
|
||||
// causing infinite loops in lookup.
|
||||
// Grow the table if the load factor would exceed 3/4 after insertion.
|
||||
// Linear probing with gap-closing deletion (Knuth Algorithm R) keeps every
|
||||
// chain compact and bounded by the table's empty-bucket count, so no
|
||||
// tombstone-driven resize is needed.
|
||||
unsigned NewNumEntries = getNumEntries() + 1;
|
||||
unsigned NumBuckets = getNumBuckets();
|
||||
if (UNLIKELY(NewNumEntries * 4 >= NumBuckets * 3)) {
|
||||
this->grow(NumBuckets * 2);
|
||||
LookupBucketFor(Lookup, TheBucket);
|
||||
NumBuckets = getNumBuckets();
|
||||
} else if (UNLIKELY(NumBuckets - (NewNumEntries + getNumTombstones()) <=
|
||||
NumBuckets / 8)) {
|
||||
this->grow(NumBuckets);
|
||||
LookupBucketFor(Lookup, TheBucket);
|
||||
}
|
||||
CHECK(TheBucket);
|
||||
|
||||
@@ -424,11 +417,6 @@ class DenseMapBase {
|
||||
// so that when growing buckets we have self-consistent entry count.
|
||||
incrementNumEntries();
|
||||
|
||||
// If we are writing over a tombstone, remember this.
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
if (!KeyInfoT::isEqual(TheBucket->getFirst(), EmptyKey))
|
||||
decrementNumTombstones();
|
||||
|
||||
return TheBucket;
|
||||
}
|
||||
|
||||
@@ -441,7 +429,6 @@ class DenseMapBase {
|
||||
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
|
||||
unsigned ProbeAmt = 1;
|
||||
while (true) {
|
||||
BucketT *Bucket = BucketsPtr + BucketNo;
|
||||
if (LIKELY(KeyInfoT::isEqual(Val, Bucket->getFirst())))
|
||||
@@ -449,10 +436,8 @@ class DenseMapBase {
|
||||
if (LIKELY(KeyInfoT::isEqual(Bucket->getFirst(), EmptyKey)))
|
||||
return nullptr;
|
||||
|
||||
// Otherwise, it's a hash collision or a tombstone, continue quadratic
|
||||
// probing.
|
||||
BucketNo += ProbeAmt++;
|
||||
BucketNo &= NumBuckets - 1;
|
||||
// Hash collision: continue linear probing.
|
||||
BucketNo = (BucketNo + 1) & (NumBuckets - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,8 +448,8 @@ class DenseMapBase {
|
||||
|
||||
/// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in
|
||||
/// FoundBucket. If the bucket contains the key and a value, this returns
|
||||
/// true, otherwise it returns a bucket with an empty marker or tombstone and
|
||||
/// returns false.
|
||||
/// true, otherwise it returns a bucket with an empty marker and returns
|
||||
/// false.
|
||||
template <typename LookupKeyT>
|
||||
bool LookupBucketFor(const LookupKeyT &Val,
|
||||
const BucketT *&FoundBucket) const {
|
||||
@@ -476,15 +461,10 @@ class DenseMapBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FoundTombstone - Keep track of whether we find a tombstone while probing.
|
||||
const BucketT *FoundTombstone = nullptr;
|
||||
const KeyT EmptyKey = getEmptyKey();
|
||||
const KeyT TombstoneKey = getTombstoneKey();
|
||||
CHECK(!KeyInfoT::isEqual(Val, EmptyKey));
|
||||
CHECK(!KeyInfoT::isEqual(Val, TombstoneKey));
|
||||
|
||||
unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
|
||||
unsigned ProbeAmt = 1;
|
||||
while (true) {
|
||||
const BucketT *ThisBucket = BucketsPtr + BucketNo;
|
||||
// Found Val's bucket? If so, return it.
|
||||
@@ -494,24 +474,14 @@ class DenseMapBase {
|
||||
}
|
||||
|
||||
// If we found an empty bucket, the key doesn't exist in the set.
|
||||
// Insert it and return the default value.
|
||||
// Return it as the insertion point.
|
||||
if (LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) {
|
||||
// If we've already seen a tombstone while probing, fill it in instead
|
||||
// of the empty bucket we eventually probed to.
|
||||
FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket;
|
||||
FoundBucket = ThisBucket;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a tombstone, remember it. If Val ends up not in the map, we
|
||||
// prefer to return it than something that would require more probing.
|
||||
if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
|
||||
!FoundTombstone)
|
||||
FoundTombstone = ThisBucket; // Remember the first tombstone found.
|
||||
|
||||
// Otherwise, it's a hash collision or a tombstone, continue quadratic
|
||||
// probing.
|
||||
BucketNo += ProbeAmt++;
|
||||
BucketNo &= (NumBuckets - 1);
|
||||
// Hash collision: continue linear probing.
|
||||
BucketNo = (BucketNo + 1) & (NumBuckets - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +557,6 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
|
||||
|
||||
BucketT *Buckets = nullptr;
|
||||
unsigned NumEntries = 0;
|
||||
unsigned NumTombstones = 0;
|
||||
unsigned NumBuckets = 0;
|
||||
|
||||
public:
|
||||
@@ -614,7 +583,6 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
|
||||
void swap(DenseMap &RHS) {
|
||||
Swap(Buckets, RHS.Buckets);
|
||||
Swap(NumEntries, RHS.NumEntries);
|
||||
Swap(NumTombstones, RHS.NumTombstones);
|
||||
Swap(NumBuckets, RHS.NumBuckets);
|
||||
}
|
||||
|
||||
@@ -639,7 +607,6 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
|
||||
this->BaseT::copyFrom(other);
|
||||
} else {
|
||||
NumEntries = 0;
|
||||
NumTombstones = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +616,6 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
|
||||
this->BaseT::initEmpty();
|
||||
} else {
|
||||
NumEntries = 0;
|
||||
NumTombstones = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,10 +641,6 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
|
||||
|
||||
void setNumEntries(unsigned Num) { NumEntries = Num; }
|
||||
|
||||
unsigned getNumTombstones() const { return NumTombstones; }
|
||||
|
||||
void setNumTombstones(unsigned Num) { NumTombstones = Num; }
|
||||
|
||||
BucketT *getBuckets() const { return Buckets; }
|
||||
|
||||
unsigned getNumBuckets() const { return NumBuckets; }
|
||||
|
||||
@@ -62,7 +62,6 @@ struct DenseMapPair {
|
||||
template <typename T>
|
||||
struct DenseMapInfo {
|
||||
// static T getEmptyKey();
|
||||
// static T getTombstoneKey();
|
||||
// static unsigned getHashValue(const T &Val);
|
||||
// static bool isEqual(const T &LHS, const T &RHS);
|
||||
};
|
||||
@@ -86,12 +85,6 @@ struct DenseMapInfo<T *> {
|
||||
return reinterpret_cast<T *>(Val);
|
||||
}
|
||||
|
||||
static constexpr T *getTombstoneKey() {
|
||||
uptr Val = static_cast<uptr>(-2);
|
||||
Val <<= Log2MaxAlign;
|
||||
return reinterpret_cast<T *>(Val);
|
||||
}
|
||||
|
||||
static constexpr unsigned getHashValue(const T *PtrVal) {
|
||||
return (unsigned((uptr)PtrVal) >> 4) ^ (unsigned((uptr)PtrVal) >> 9);
|
||||
}
|
||||
@@ -105,7 +98,6 @@ struct DenseMapInfo<T *> {
|
||||
template <>
|
||||
struct DenseMapInfo<char> {
|
||||
static constexpr char getEmptyKey() { return ~0; }
|
||||
static constexpr char getTombstoneKey() { return ~0 - 1; }
|
||||
static constexpr unsigned getHashValue(const char &Val) { return Val * 37U; }
|
||||
|
||||
static constexpr bool isEqual(const char &LHS, const char &RHS) {
|
||||
@@ -117,7 +109,6 @@ struct DenseMapInfo<char> {
|
||||
template <>
|
||||
struct DenseMapInfo<unsigned char> {
|
||||
static constexpr unsigned char getEmptyKey() { return ~0; }
|
||||
static constexpr unsigned char getTombstoneKey() { return ~0 - 1; }
|
||||
static constexpr unsigned getHashValue(const unsigned char &Val) {
|
||||
return Val * 37U;
|
||||
}
|
||||
@@ -132,7 +123,6 @@ struct DenseMapInfo<unsigned char> {
|
||||
template <>
|
||||
struct DenseMapInfo<unsigned short> {
|
||||
static constexpr unsigned short getEmptyKey() { return 0xFFFF; }
|
||||
static constexpr unsigned short getTombstoneKey() { return 0xFFFF - 1; }
|
||||
static constexpr unsigned getHashValue(const unsigned short &Val) {
|
||||
return Val * 37U;
|
||||
}
|
||||
@@ -147,7 +137,6 @@ struct DenseMapInfo<unsigned short> {
|
||||
template <>
|
||||
struct DenseMapInfo<unsigned> {
|
||||
static constexpr unsigned getEmptyKey() { return ~0U; }
|
||||
static constexpr unsigned getTombstoneKey() { return ~0U - 1; }
|
||||
static constexpr unsigned getHashValue(const unsigned &Val) {
|
||||
return Val * 37U;
|
||||
}
|
||||
@@ -161,7 +150,6 @@ struct DenseMapInfo<unsigned> {
|
||||
template <>
|
||||
struct DenseMapInfo<unsigned long> {
|
||||
static constexpr unsigned long getEmptyKey() { return ~0UL; }
|
||||
static constexpr unsigned long getTombstoneKey() { return ~0UL - 1L; }
|
||||
|
||||
static constexpr unsigned getHashValue(const unsigned long &Val) {
|
||||
return (unsigned)(Val * 37UL);
|
||||
@@ -177,7 +165,6 @@ struct DenseMapInfo<unsigned long> {
|
||||
template <>
|
||||
struct DenseMapInfo<unsigned long long> {
|
||||
static constexpr unsigned long long getEmptyKey() { return ~0ULL; }
|
||||
static constexpr unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; }
|
||||
|
||||
static constexpr unsigned getHashValue(const unsigned long long &Val) {
|
||||
return (unsigned)(Val * 37ULL);
|
||||
@@ -193,7 +180,6 @@ struct DenseMapInfo<unsigned long long> {
|
||||
template <>
|
||||
struct DenseMapInfo<short> {
|
||||
static constexpr short getEmptyKey() { return 0x7FFF; }
|
||||
static constexpr short getTombstoneKey() { return -0x7FFF - 1; }
|
||||
static constexpr unsigned getHashValue(const short &Val) { return Val * 37U; }
|
||||
static constexpr bool isEqual(const short &LHS, const short &RHS) {
|
||||
return LHS == RHS;
|
||||
@@ -204,7 +190,6 @@ struct DenseMapInfo<short> {
|
||||
template <>
|
||||
struct DenseMapInfo<int> {
|
||||
static constexpr int getEmptyKey() { return 0x7fffffff; }
|
||||
static constexpr int getTombstoneKey() { return -0x7fffffff - 1; }
|
||||
static constexpr unsigned getHashValue(const int &Val) {
|
||||
return (unsigned)(Val * 37U);
|
||||
}
|
||||
@@ -221,8 +206,6 @@ struct DenseMapInfo<long> {
|
||||
return (1UL << (sizeof(long) * 8 - 1)) - 1UL;
|
||||
}
|
||||
|
||||
static constexpr long getTombstoneKey() { return getEmptyKey() - 1L; }
|
||||
|
||||
static constexpr unsigned getHashValue(const long &Val) {
|
||||
return (unsigned)(Val * 37UL);
|
||||
}
|
||||
@@ -236,9 +219,6 @@ struct DenseMapInfo<long> {
|
||||
template <>
|
||||
struct DenseMapInfo<long long> {
|
||||
static constexpr long long getEmptyKey() { return 0x7fffffffffffffffLL; }
|
||||
static constexpr long long getTombstoneKey() {
|
||||
return -0x7fffffffffffffffLL - 1;
|
||||
}
|
||||
|
||||
static constexpr unsigned getHashValue(const long long &Val) {
|
||||
return (unsigned)(Val * 37ULL);
|
||||
@@ -261,11 +241,6 @@ struct DenseMapInfo<detail::DenseMapPair<T, U>> {
|
||||
SecondInfo::getEmptyKey());
|
||||
}
|
||||
|
||||
static constexpr Pair getTombstoneKey() {
|
||||
return detail::DenseMapPair<T, U>(FirstInfo::getTombstoneKey(),
|
||||
SecondInfo::getTombstoneKey());
|
||||
}
|
||||
|
||||
static constexpr unsigned getHashValue(const Pair &PairVal) {
|
||||
return detail::combineHashValue(FirstInfo::getHashValue(PairVal.first),
|
||||
SecondInfo::getHashValue(PairVal.second));
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
# define __errno_location _errno
|
||||
#elif SANITIZER_HAIKU
|
||||
# define __errno_location _errnop
|
||||
#elif SANITIZER_AIX
|
||||
# define __errno_location _Errno
|
||||
#endif
|
||||
|
||||
extern "C" int *__errno_location();
|
||||
|
||||
+2
-2
@@ -189,8 +189,8 @@ class FlagParser {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
|
||||
T *var) {
|
||||
void RegisterFlag(FlagParser* parser, const char* name, const char* desc,
|
||||
T* var) {
|
||||
FlagHandler<T> *fh = new (GetGlobalLowLevelAllocator()) FlagHandler<T>(var);
|
||||
parser->RegisterHandler(name, fh, desc);
|
||||
}
|
||||
|
||||
+2
-2
@@ -93,8 +93,8 @@ void CheckMPROTECT() {}
|
||||
void PlatformPrepareForSandboxing(void *args) {}
|
||||
void DisableCoreDumperIfNecessary() {}
|
||||
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
|
||||
void SetAlternateSignalStack() {}
|
||||
void UnsetAlternateSignalStack() {}
|
||||
void* SetAlternateSignalStack() { return nullptr; }
|
||||
void UnsetAlternateSignalStack(void* altstack_base) {}
|
||||
|
||||
bool SignalContext::IsStackOverflow() const { return false; }
|
||||
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
|
||||
|
||||
@@ -128,6 +128,10 @@ uptr internal_close(fd_t fd) {
|
||||
RETURN_AND_SET_ERRNO(_kern_close(fd));
|
||||
}
|
||||
|
||||
uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
|
||||
return -1; // Not supported.
|
||||
}
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
CHECK(&_kern_open);
|
||||
RETURN_AND_SET_ERRNO(_kern_open(-1, filename, flags, 0));
|
||||
|
||||
@@ -76,11 +76,6 @@ void __sanitizer_annotate_double_ended_contiguous_container(
|
||||
const void *old_container_beg, const void *old_container_end,
|
||||
const void *new_container_beg, const void *new_container_end);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_copy_contiguous_container_annotations(const void *src_begin,
|
||||
const void *src_end,
|
||||
const void *dst_begin,
|
||||
const void *dst_end);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
|
||||
const void *end);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
|
||||
+16
-12
@@ -29,20 +29,24 @@
|
||||
|
||||
// Only use SANITIZER_*ATTRIBUTE* before the function return type!
|
||||
#if SANITIZER_WINDOWS
|
||||
#if SANITIZER_IMPORT_INTERFACE
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllimport)
|
||||
#else
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
|
||||
#endif
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_IMPORT
|
||||
#elif SANITIZER_GO
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
# if SANITIZER_IMPORT_INTERFACE
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllimport)
|
||||
# else
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
|
||||
# endif
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_IMPORT
|
||||
#else
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
|
||||
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
|
||||
# if SANITIZER_GO
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
# elif SANITIZER_AMDGPU || SANITIZER_NVPTX
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("hidden")))
|
||||
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
|
||||
# else
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
|
||||
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
|
||||
# endif // SANITIZER_GO
|
||||
# if SANITIZER_APPLE
|
||||
# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak_import))
|
||||
# else
|
||||
|
||||
+81
-12
@@ -90,10 +90,18 @@
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE const char *strerrorname_np(int);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LINUX && defined(__loongarch__)
|
||||
# if SANITIZER_LINUX && \
|
||||
(defined(__loongarch__) || defined(__hexagon__) || defined(__alpha__))
|
||||
# include <sys/sysmacros.h>
|
||||
# endif
|
||||
|
||||
// Hexagon uses statx() instead of stat64(). glibc provides struct statx
|
||||
// through <sys/stat.h>, but musl does not — pull it from <linux/stat.h>.
|
||||
// On this musl/hexagon combination the two headers coexist without conflict.
|
||||
# if SANITIZER_LINUX && defined(__hexagon__)
|
||||
# include <linux/stat.h>
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LINUX && defined(__powerpc64__)
|
||||
# include <asm/ptrace.h>
|
||||
# endif
|
||||
@@ -254,6 +262,8 @@ ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); }
|
||||
# include "sanitizer_syscall_linux_hexagon.inc"
|
||||
# elif SANITIZER_LINUX && SANITIZER_LOONGARCH64
|
||||
# include "sanitizer_syscall_linux_loongarch64.inc"
|
||||
# elif SANITIZER_LINUX && SANITIZER_ALPHA
|
||||
# include "sanitizer_syscall_linux_alpha.inc"
|
||||
# else
|
||||
# include "sanitizer_syscall_generic.inc"
|
||||
# endif
|
||||
@@ -296,11 +306,13 @@ int internal_madvise(uptr addr, uptr length, int advice) {
|
||||
return internal_syscall(SYSCALL(madvise), addr, length, advice);
|
||||
}
|
||||
|
||||
# if SANITIZER_FREEBSD
|
||||
uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
|
||||
# if SANITIZER_FREEBSD || (SANITIZER_LINUX && defined(__NR_close_range))
|
||||
return internal_syscall(SYSCALL(close_range), lowfd, highfd, flags);
|
||||
}
|
||||
# endif
|
||||
return -1; // Not supported.
|
||||
}
|
||||
|
||||
uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); }
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
@@ -341,7 +353,8 @@ uptr internal_ftruncate(fd_t fd, uptr size) {
|
||||
return res;
|
||||
}
|
||||
|
||||
# if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX
|
||||
# if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX && \
|
||||
!defined(__hexagon__)
|
||||
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
|
||||
internal_memset(out, 0, sizeof(*out));
|
||||
out->st_dev = in->st_dev;
|
||||
@@ -360,7 +373,8 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) {
|
||||
}
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LINUX && defined(__loongarch__)
|
||||
# if SANITIZER_LINUX && \
|
||||
(defined(__loongarch__) || defined(__hexagon__) || defined(__alpha__))
|
||||
static void statx_to_stat(struct statx *in, struct stat *out) {
|
||||
internal_memset(out, 0, sizeof(*out));
|
||||
out->st_dev = makedev(in->stx_dev_major, in->stx_dev_minor);
|
||||
@@ -440,7 +454,7 @@ uptr internal_stat(const char *path, void *buf) {
|
||||
# if SANITIZER_FREEBSD
|
||||
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0);
|
||||
# elif SANITIZER_LINUX
|
||||
# if defined(__loongarch__)
|
||||
# if defined(__loongarch__) || defined(__hexagon__) || defined(__alpha__)
|
||||
struct statx bufx;
|
||||
int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path,
|
||||
AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx);
|
||||
@@ -478,7 +492,7 @@ uptr internal_lstat(const char *path, void *buf) {
|
||||
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
|
||||
AT_SYMLINK_NOFOLLOW);
|
||||
# elif SANITIZER_LINUX
|
||||
# if defined(__loongarch__)
|
||||
# if defined(__loongarch__) || defined(__hexagon__) || defined(__alpha__)
|
||||
struct statx bufx;
|
||||
int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path,
|
||||
AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT,
|
||||
@@ -526,7 +540,7 @@ uptr internal_fstat(fd_t fd, void *buf) {
|
||||
int res = internal_syscall(SYSCALL(fstat64), fd, &kbuf);
|
||||
kernel_stat_to_stat(&kbuf, (struct stat *)buf);
|
||||
return res;
|
||||
# elif SANITIZER_LINUX && defined(__loongarch__)
|
||||
# elif SANITIZER_LINUX && (defined(__loongarch__) || defined(__alpha__))
|
||||
struct statx bufx;
|
||||
int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH,
|
||||
STATX_BASIC_STATS, (uptr)&bufx);
|
||||
@@ -535,6 +549,13 @@ uptr internal_fstat(fd_t fd, void *buf) {
|
||||
# else
|
||||
return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
|
||||
# endif
|
||||
# elif SANITIZER_LINUX && defined(__hexagon__)
|
||||
// Hexagon musl lacks struct stat64; use statx() instead.
|
||||
struct statx bufx;
|
||||
int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH,
|
||||
STATX_BASIC_STATS, (uptr)&bufx);
|
||||
statx_to_stat(&bufx, (struct stat*)buf);
|
||||
return res;
|
||||
# else
|
||||
struct stat64 buf64;
|
||||
int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
|
||||
@@ -1003,7 +1024,7 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
|
||||
// rt_sigaction, so we need to do the same (we'll need to reimplement the
|
||||
// restorers; for x86_64 the restorer address can be obtained from
|
||||
// oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).
|
||||
# if !SANITIZER_ANDROID || !SANITIZER_MIPS32
|
||||
# if (!SANITIZER_ANDROID || !SANITIZER_MIPS32) && !defined(__alpha__)
|
||||
k_act.sa_restorer = u_act->sa_restorer;
|
||||
# endif
|
||||
}
|
||||
@@ -1019,7 +1040,7 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
|
||||
internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,
|
||||
sizeof(__sanitizer_kernel_sigset_t));
|
||||
u_oldact->sa_flags = k_oldact.sa_flags;
|
||||
# if !SANITIZER_ANDROID || !SANITIZER_MIPS32
|
||||
# if (!SANITIZER_ANDROID || !SANITIZER_MIPS32) && !defined(__alpha__)
|
||||
u_oldact->sa_restorer = k_oldact.sa_restorer;
|
||||
# endif
|
||||
}
|
||||
@@ -1228,6 +1249,16 @@ uptr GetMaxVirtualAddress() {
|
||||
// loongarch64 also has multiple address space layouts: default is 47-bit.
|
||||
// RISC-V 64 also has multiple address space layouts: 39, 48 and 57-bit.
|
||||
return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
|
||||
# elif SANITIZER_ALPHA
|
||||
// Linux/Alpha uses a 42-bit user VAS (TASK_SIZE = 0x40000000000). With
|
||||
// fixed shadow offset 0x10000000000 (1 TiB) the layout is:
|
||||
// LowMem: [0x000000000000, 0x00ffffffffff] (1 TiB)
|
||||
// LowShadow: [0x010000000000, 0x011fffffffff] (128 GiB)
|
||||
// ShadowGap: [0x012000000000, 0x012fffffffff]
|
||||
// HighShadow:[0x013000000000, 0x017fffffffff] (256 GiB)
|
||||
// HighMem: [0x018000000000, 0x03ffffffffff] (2.5 TiB, stack near top)
|
||||
// Capping at TASK_SIZE - 1 avoids treating kernel addresses as HighMem.
|
||||
return (1ULL << 42) - 1; // TASK_SIZE - 1
|
||||
# elif SANITIZER_MIPS64
|
||||
return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
|
||||
# elif defined(__s390x__)
|
||||
@@ -1894,6 +1925,39 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
: "memory");
|
||||
return res;
|
||||
}
|
||||
# elif defined(__hexagon__)
|
||||
uptr internal_clone(int (*fn)(void*), void* child_stack, int flags, void* arg,
|
||||
int* parent_tidptr, void* newtls, int* child_tidptr) {
|
||||
if (!fn || !child_stack)
|
||||
return -EINVAL;
|
||||
child_stack = (char*)child_stack - 2 * sizeof(unsigned int);
|
||||
((unsigned int*)child_stack)[0] = (uptr)fn;
|
||||
((unsigned int*)child_stack)[1] = (uptr)arg;
|
||||
|
||||
// Hexagon clone syscall uses the generic argument order (no
|
||||
// CONFIG_CLONE_BACKWARDS): flags, stack, ptid, ctid, tls.
|
||||
register int r0 __asm__("r0") = flags;
|
||||
register void* r1 __asm__("r1") = child_stack;
|
||||
register int* r2 __asm__("r2") = parent_tidptr;
|
||||
register int* r3 __asm__("r3") = child_tidptr;
|
||||
register void* r4 __asm__("r4") = newtls;
|
||||
register int r6 __asm__("r6") = __NR_clone;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"trap0(#1)\n" /* syscall */
|
||||
"{ p0 = cmp.eq(r0, #0)\n" /* child? */
|
||||
" if (!p0.new) jump:nt 1f }\n"
|
||||
"r1 = memw(r29 + #0)\n" /* r1 = fn */
|
||||
"r0 = memw(r29 + #4)\n" /* r0 = arg */
|
||||
"callr r1\n" /* fn(arg) */
|
||||
"r6 = #%7\n" /* __NR_exit */
|
||||
"trap0(#1)\n"
|
||||
"1:\n"
|
||||
: "=r"(r0)
|
||||
: "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r6), "i"(__NR_exit)
|
||||
: "memory", "p0", "r1", "lr");
|
||||
return (uptr)r0;
|
||||
}
|
||||
# endif
|
||||
# endif // SANITIZER_LINUX
|
||||
|
||||
@@ -2427,7 +2491,7 @@ static void DumpSingleReg(ucontext_t *ctx, int RegNum) {
|
||||
# if SANITIZER_LINUX
|
||||
ctx->uc_mcontext.gregs[RegNum]
|
||||
# elif SANITIZER_NETBSD
|
||||
ctx->uc_mcontext.__gregs[RegNum]
|
||||
(unsigned long long)ctx->uc_mcontext.__gregs[RegNum]
|
||||
# endif
|
||||
);
|
||||
# elif defined(__i386__)
|
||||
@@ -2729,6 +2793,11 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
*pc = ucontext->uc_mcontext.__pc;
|
||||
*bp = ucontext->uc_mcontext.__gregs[22];
|
||||
*sp = ucontext->uc_mcontext.__gregs[3];
|
||||
# elif defined(__alpha__)
|
||||
ucontext_t* ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.sc_pc;
|
||||
*bp = ucontext->uc_mcontext.sc_regs[15]; // $fp / $s6
|
||||
*sp = ucontext->uc_mcontext.sc_regs[30]; // $sp
|
||||
# else
|
||||
# error "Unsupported arch"
|
||||
# endif
|
||||
@@ -2819,7 +2888,7 @@ void CheckMPROTECT() {
|
||||
# endif
|
||||
}
|
||||
|
||||
void CheckNoDeepBind(const char *filename, int flag) {
|
||||
void OnDlOpen(const char* filename, int flag) {
|
||||
# ifdef RTLD_DEEPBIND
|
||||
if (flag & RTLD_DEEPBIND) {
|
||||
Report(
|
||||
|
||||
+2
-1
@@ -86,7 +86,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
|
||||
# if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \
|
||||
defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \
|
||||
defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64
|
||||
defined(__arm__) || defined(__hexagon__) || SANITIZER_RISCV64 || \
|
||||
SANITIZER_LOONGARCH64
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
int *parent_tidptr, void *newtls, int *child_tidptr);
|
||||
# endif
|
||||
|
||||
@@ -284,6 +284,10 @@ static uptr ThreadDescriptorSizeFallback() {
|
||||
# if defined(__powerpc64__)
|
||||
return 1776; // from glibc.ppc64le 2.20-8.fc21
|
||||
# endif
|
||||
|
||||
# if defined(__alpha__)
|
||||
return 1824; // from glibc 2.43
|
||||
# endif
|
||||
}
|
||||
# endif // SANITIZER_GLIBC && !SANITIZER_GO
|
||||
|
||||
@@ -494,10 +498,10 @@ __attribute__((unused)) static void GetStaticTlsBoundary(uptr *addr, uptr *size,
|
||||
// loader places static TLS blocks this way not to waste space.
|
||||
uptr l = one;
|
||||
*align = ranges[l].align;
|
||||
while (l != 0 && ranges[l].begin < ranges[l - 1].end + ranges[l].align)
|
||||
while (l != 0 && ranges[l].begin <= ranges[l - 1].end + ranges[l].align)
|
||||
*align = Max(*align, ranges[--l].align);
|
||||
uptr r = one + 1;
|
||||
while (r != len && ranges[r].begin < ranges[r - 1].end + ranges[r].align)
|
||||
while (r != len && ranges[r].begin <= ranges[r - 1].end + ranges[r].align)
|
||||
*align = Max(*align, ranges[r++].align);
|
||||
*addr = ranges[l].begin;
|
||||
*size = ranges[r - 1].end - ranges[l].begin;
|
||||
|
||||
+1
-3
@@ -26,9 +26,7 @@ ItOut LzwEncode(ItIn begin, ItIn end, ItOut out) {
|
||||
|
||||
// Sentinel value for substrings of len 1.
|
||||
static constexpr LzwCodeType kNoPrefix =
|
||||
Min(DenseMapInfo<Substring>::getEmptyKey().first,
|
||||
DenseMapInfo<Substring>::getTombstoneKey().first) -
|
||||
1;
|
||||
DenseMapInfo<Substring>::getEmptyKey().first - 1;
|
||||
DenseMap<Substring, LzwCodeType> prefix_to_code;
|
||||
{
|
||||
// Add all substring of len 1 as initial dictionary.
|
||||
|
||||
+30
-2
@@ -170,6 +170,10 @@ uptr internal_close(fd_t fd) {
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
|
||||
return -1; // Not supported.
|
||||
}
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
return open(filename, flags);
|
||||
}
|
||||
@@ -609,7 +613,13 @@ static uptr ApproximateOSVersionViaKernelVersion(VersStr vers) {
|
||||
u16 os_major = kernel_major - offset;
|
||||
|
||||
const char *format = "%d.0";
|
||||
if (TARGET_OS_OSX) {
|
||||
if (kernel_major >= 27) {
|
||||
// with kernel major 27 <=> OSes 27.0, OS versions are aligned with kernel
|
||||
os_major = kernel_major;
|
||||
} else if (kernel_major >= 25) {
|
||||
// with kernel_major 25 <=> OSes 26.0, OS versions are aligned
|
||||
os_major = kernel_major + 1;
|
||||
} else if (TARGET_OS_OSX) {
|
||||
if (os_major >= 16) { // macOS 11+
|
||||
os_major -= 5;
|
||||
} else { // macOS 10.15 and below
|
||||
@@ -666,6 +676,24 @@ static void MapToMacos(u16 *major, u16 *minor) {
|
||||
if (TARGET_OS_OSX)
|
||||
return;
|
||||
|
||||
// All supported platforms (including DriverKit) have
|
||||
// aligned version numbers in macOS 27+
|
||||
if (*major >= 27)
|
||||
return;
|
||||
|
||||
# if TARGET_OS_DRIVERKIT
|
||||
// Driverkit 25.0+ aligns with macOS 26+
|
||||
if (*major >= 25) {
|
||||
*major += 1;
|
||||
return;
|
||||
}
|
||||
# else
|
||||
// macOS 26 and later have aligned version strings.
|
||||
if (*major >= 26)
|
||||
return;
|
||||
# endif
|
||||
|
||||
// Below are mappings for pre-macOS-25-aligned releases
|
||||
if (TARGET_OS_IOS || TARGET_OS_TV)
|
||||
*major += 2;
|
||||
else if (TARGET_OS_WATCH)
|
||||
@@ -1529,7 +1557,7 @@ void DumpProcessMap() {
|
||||
Printf("End of module map.\n");
|
||||
}
|
||||
|
||||
void CheckNoDeepBind(const char *filename, int flag) {
|
||||
void OnDlOpen(const char* filename, int flag) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,10 @@ uptr internal_close(fd_t fd) {
|
||||
return _sys_close(fd);
|
||||
}
|
||||
|
||||
uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
|
||||
return -1; // Not supported.
|
||||
}
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
CHECK(&_sys_open);
|
||||
return _sys_open(filename, flags);
|
||||
|
||||
+33
-2
@@ -15,7 +15,8 @@
|
||||
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
|
||||
!defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) && \
|
||||
!(defined(__sun__) && defined(__svr4__)) && !defined(__HAIKU__) && \
|
||||
!defined(__wasi__)
|
||||
!defined(__wasi__) && !defined(__NVPTX__) && !defined(__AMDGPU__) && \
|
||||
!defined(__SPIRV__) && !defined(_AIX)
|
||||
# error "This operating system is not supported"
|
||||
#endif
|
||||
|
||||
@@ -32,6 +33,12 @@
|
||||
# define SANITIZER_LINUX 0
|
||||
#endif
|
||||
|
||||
#if defined(_AIX)
|
||||
# define SANITIZER_AIX 1
|
||||
#else
|
||||
# define SANITIZER_AIX 0
|
||||
#endif
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
# define SANITIZER_GLIBC 1
|
||||
#else
|
||||
@@ -151,7 +158,7 @@
|
||||
|
||||
#define SANITIZER_POSIX \
|
||||
(SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_APPLE || \
|
||||
SANITIZER_NETBSD || SANITIZER_SOLARIS || SANITIZER_HAIKU)
|
||||
SANITIZER_NETBSD || SANITIZER_SOLARIS || SANITIZER_HAIKU || SANITIZER_AIX)
|
||||
|
||||
#if __LP64__ || defined(_WIN64)
|
||||
# define SANITIZER_WORDSIZE 64
|
||||
@@ -302,6 +309,30 @@
|
||||
# define SANITIZER_LOONGARCH64 0
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__)
|
||||
# define SANITIZER_ALPHA 1
|
||||
#else
|
||||
# define SANITIZER_ALPHA 0
|
||||
#endif
|
||||
|
||||
#if defined(__AMDGPU__)
|
||||
# define SANITIZER_AMDGPU 1
|
||||
#else
|
||||
# define SANITIZER_AMDGPU 0
|
||||
#endif
|
||||
|
||||
#if defined(__NVPTX__)
|
||||
# define SANITIZER_NVPTX 1
|
||||
#else
|
||||
# define SANITIZER_NVPTX 0
|
||||
#endif
|
||||
|
||||
#if defined(__SPIRV__)
|
||||
# define SANITIZER_SPIRV 1
|
||||
#else
|
||||
# define SANITIZER_SPIRV 0
|
||||
#endif
|
||||
|
||||
// By default we allow to use SizeClassAllocator64 on 64-bit platform.
|
||||
// But in some cases SizeClassAllocator64 does not work well and we need to
|
||||
// fallback to SizeClassAllocator32.
|
||||
|
||||
@@ -201,6 +201,9 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
|
||||
|
||||
#define SANITIZER_INTERCEPT_READ SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PREAD SI_POSIX
|
||||
#define SANITIZER_INTERCEPT___READ_CHK SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT___PREAD_CHK SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT___PREAD64_CHK SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_WRITE SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PWRITE SI_POSIX
|
||||
|
||||
@@ -393,6 +396,8 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
|
||||
#define SANITIZER_INTERCEPT_SHMCTL \
|
||||
(((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64) || \
|
||||
SI_NETBSD || SI_SOLARIS)
|
||||
// shmat calls REAL(shmctl), so it requires shmctl interception.
|
||||
#define SANITIZER_INTERCEPT_SHMAT SANITIZER_INTERCEPT_SHMCTL
|
||||
#define SANITIZER_INTERCEPT_RANDOM_R SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
|
||||
@@ -545,7 +550,7 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
|
||||
#define SANITIZER_INTERCEPT___LIBC_MEMALIGN SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_PVALLOC (SI_GLIBC || SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_CFREE (SI_GLIBC && !SANITIZER_RISCV64)
|
||||
#define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_REALLOCARRAY (SI_POSIX || SI_FUCHSIA)
|
||||
#define SANITIZER_INTERCEPT_ALIGNED_ALLOC \
|
||||
(!SI_MAC || SI_MAC_SDK_10_15_AVAILABLE)
|
||||
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD)
|
||||
|
||||
@@ -60,7 +60,7 @@ using namespace __sanitizer;
|
||||
# if !defined(__powerpc64__) && !defined(__x86_64__) && \
|
||||
!defined(__aarch64__) && !defined(__mips__) && !defined(__s390__) && \
|
||||
!defined(__sparc__) && !defined(__riscv) && !defined(__hexagon__) && \
|
||||
!defined(__loongarch__)
|
||||
!defined(__loongarch__) && !defined(__alpha__)
|
||||
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
|
||||
#endif
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
// Must go after undef _FILE_OFFSET_BITS.
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_APPLE || SANITIZER_HAIKU
|
||||
#if SANITIZER_LINUX || SANITIZER_APPLE || SANITIZER_HAIKU || SANITIZER_AIX
|
||||
// Must go after undef _FILE_OFFSET_BITS.
|
||||
#include "sanitizer_glibc_version.h"
|
||||
|
||||
@@ -61,11 +61,11 @@
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
#if !SANITIZER_HAIKU
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#include <sys/timeb.h>
|
||||
#include <utmpx.h>
|
||||
# if !SANITIZER_HAIKU && !SANITIZER_AIX
|
||||
# include <sys/mount.h>
|
||||
# endif
|
||||
# include <sys/timeb.h>
|
||||
# include <utmpx.h>
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
@@ -113,11 +113,15 @@ typedef struct user_fpregs elf_fpregset_t;
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
#include <ifaddrs.h>
|
||||
#if !SANITIZER_HAIKU
|
||||
#include <sys/ucontext.h>
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
# if !SANITIZER_AIX
|
||||
# include <ifaddrs.h>
|
||||
# else
|
||||
# include <netinet/in.h>
|
||||
# endif
|
||||
# if !SANITIZER_HAIKU
|
||||
# include <sys/ucontext.h>
|
||||
# include <wordexp.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
@@ -182,6 +186,17 @@ typedef struct user_fpregs elf_fpregset_t;
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
# if SANITIZER_AIX
|
||||
# include <netinet/ip_mroute.h>
|
||||
# include <stropts.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/statfs.h>
|
||||
# include <unistd.h>
|
||||
# if HAVE_RPC_XDR_H
|
||||
# include <tirpc/rpc/xdr.h>
|
||||
# endif
|
||||
# endif
|
||||
|
||||
// Include these after system headers to avoid name clashes and ambiguities.
|
||||
# include "sanitizer_common.h"
|
||||
# include "sanitizer_internal_defs.h"
|
||||
@@ -293,7 +308,7 @@ namespace __sanitizer {
|
||||
#define SIZEOF_STRUCT_USTAT 32
|
||||
# elif defined(__arm__) || defined(__i386__) || defined(__mips__) || \
|
||||
defined(__powerpc__) || defined(__s390__) || defined(__sparc__) || \
|
||||
defined(__hexagon__)
|
||||
defined(__hexagon__) || defined(__alpha__)
|
||||
# define SIZEOF_STRUCT_USTAT 20
|
||||
# elif defined(__loongarch__)
|
||||
// Not used. The minimum Glibc version available for LoongArch is 2.36
|
||||
@@ -305,9 +320,12 @@ namespace __sanitizer {
|
||||
unsigned struct_ustat_sz = SIZEOF_STRUCT_USTAT;
|
||||
unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
|
||||
unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
|
||||
#endif // SANITIZER_GLIBC
|
||||
# elif SANITIZER_MUSL
|
||||
// On musl, rlimit64 is an alias for rlimit.
|
||||
unsigned struct_rlimit64_sz = sizeof(struct rlimit);
|
||||
# endif // SANITIZER_GLIBC
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
unsigned struct_timex_sz = sizeof(struct timex);
|
||||
unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
|
||||
unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
|
||||
@@ -556,13 +574,13 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||
|
||||
unsigned IOCTL_FIONBIO = FIONBIO;
|
||||
#if !SANITIZER_HAIKU
|
||||
# if !SANITIZER_HAIKU
|
||||
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
||||
unsigned IOCTL_FIOCLEX = FIOCLEX;
|
||||
unsigned IOCTL_FIOGETOWN = FIOGETOWN;
|
||||
unsigned IOCTL_FIONCLEX = FIONCLEX;
|
||||
unsigned IOCTL_FIOSETOWN = FIOSETOWN;
|
||||
#endif
|
||||
# endif
|
||||
unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
|
||||
unsigned IOCTL_SIOCATMARK = SIOCATMARK;
|
||||
unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
|
||||
@@ -584,14 +602,14 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
|
||||
unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
|
||||
|
||||
#if !SANITIZER_HAIKU
|
||||
# if !SANITIZER_HAIKU
|
||||
unsigned IOCTL_TIOCCONS = TIOCCONS;
|
||||
unsigned IOCTL_TIOCGETD = TIOCGETD;
|
||||
unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
|
||||
unsigned IOCTL_TIOCPKT = TIOCPKT;
|
||||
unsigned IOCTL_TIOCSETD = TIOCSETD;
|
||||
unsigned IOCTL_TIOCSTI = TIOCSTI;
|
||||
#endif
|
||||
# endif
|
||||
|
||||
unsigned IOCTL_TIOCEXCL = TIOCEXCL;
|
||||
unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
|
||||
@@ -602,10 +620,12 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
unsigned IOCTL_TIOCMSET = TIOCMSET;
|
||||
unsigned IOCTL_TIOCNXCL = TIOCNXCL;
|
||||
unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
|
||||
# if !SANITIZER_AIX
|
||||
unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
|
||||
# endif
|
||||
unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
|
||||
unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
|
||||
unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
|
||||
#endif
|
||||
@@ -1067,6 +1087,9 @@ CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
|
||||
# if SANITIZER_AIX
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_eflags);
|
||||
# endif
|
||||
|
||||
CHECK_TYPE_SIZE(hostent);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_name);
|
||||
@@ -1113,11 +1136,13 @@ COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_ino);
|
||||
#if SANITIZER_APPLE
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_seekoff);
|
||||
#elif SANITIZER_FREEBSD || SANITIZER_HAIKU
|
||||
# elif SANITIZER_AIX
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_offset);
|
||||
# elif SANITIZER_FREEBSD || SANITIZER_HAIKU
|
||||
// There is no 'd_off' field on FreeBSD.
|
||||
#else
|
||||
# else
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_off);
|
||||
#endif
|
||||
# endif
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
|
||||
|
||||
#if SANITIZER_GLIBC
|
||||
@@ -1151,7 +1176,8 @@ CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
|
||||
// didn't exist.
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
|
||||
#endif
|
||||
#if SANITIZER_LINUX && (!SANITIZER_ANDROID || !SANITIZER_MIPS32)
|
||||
# if SANITIZER_LINUX && (!SANITIZER_ANDROID || !SANITIZER_MIPS32) && \
|
||||
!defined(__alpha__)
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer);
|
||||
#endif
|
||||
|
||||
@@ -1192,6 +1218,10 @@ CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
|
||||
#endif
|
||||
# if SANITIZER_AIX
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_sflags);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_soffs);
|
||||
# endif
|
||||
|
||||
CHECK_TYPE_SIZE(tm);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_sec);
|
||||
@@ -1203,10 +1233,12 @@ CHECK_SIZE_AND_OFFSET(tm, tm_year);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_wday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_yday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
|
||||
# if !SANITIZER_AIX
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_zone);
|
||||
# endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
# if SANITIZER_LINUX
|
||||
CHECK_TYPE_SIZE(mntent);
|
||||
CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname);
|
||||
CHECK_SIZE_AND_OFFSET(mntent, mnt_dir);
|
||||
@@ -1256,7 +1288,7 @@ CHECK_TYPE_SIZE(clock_t);
|
||||
CHECK_TYPE_SIZE(clockid_t);
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_ANDROID && !SANITIZER_HAIKU
|
||||
# if !SANITIZER_ANDROID && !SANITIZER_HAIKU && !SANITIZER_AIX
|
||||
CHECK_TYPE_SIZE(ifaddrs);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
|
||||
|
||||
+69
-13
@@ -14,7 +14,7 @@
|
||||
#ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H
|
||||
#define SANITIZER_PLATFORM_LIMITS_POSIX_H
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_APPLE || SANITIZER_HAIKU
|
||||
#if SANITIZER_LINUX || SANITIZER_APPLE || SANITIZER_HAIKU || SANITIZER_AIX
|
||||
|
||||
# include "sanitizer_internal_defs.h"
|
||||
# include "sanitizer_mallinfo.h"
|
||||
@@ -29,7 +29,7 @@
|
||||
# define SANITIZER_HAS_STAT64 0
|
||||
# define SANITIZER_HAS_STATFS64 0
|
||||
# endif
|
||||
# elif SANITIZER_GLIBC || SANITIZER_ANDROID
|
||||
# elif SANITIZER_GLIBC || SANITIZER_ANDROID || SANITIZER_AIX
|
||||
# define SANITIZER_HAS_STAT64 1
|
||||
# define SANITIZER_HAS_STATFS64 1
|
||||
# elif SANITIZER_HAIKU
|
||||
@@ -103,9 +103,15 @@ const unsigned struct_kernel_stat64_sz = 104;
|
||||
const unsigned struct_kernel_stat_sz = SANITIZER_ANDROID
|
||||
? FIRST_32_SECOND_64(104, 128)
|
||||
# if defined(_ABIN32) && _MIPS_SIM == _ABIN32
|
||||
# if defined(_TIME_BITS) && _TIME_BITS == 64
|
||||
: FIRST_32_SECOND_64(112, 216);
|
||||
# else
|
||||
: FIRST_32_SECOND_64(176, 216);
|
||||
# endif
|
||||
# elif SANITIZER_MUSL
|
||||
: FIRST_32_SECOND_64(160, 208);
|
||||
# elif defined(_TIME_BITS) && _TIME_BITS == 64
|
||||
: FIRST_32_SECOND_64(112, 216);
|
||||
# else
|
||||
: FIRST_32_SECOND_64(160, 216);
|
||||
# endif
|
||||
@@ -133,6 +139,9 @@ const unsigned struct_kernel_stat64_sz = 0;
|
||||
# elif defined(__loongarch__)
|
||||
const unsigned struct_kernel_stat_sz = 128;
|
||||
const unsigned struct_kernel_stat64_sz = 0;
|
||||
# elif defined(__alpha__)
|
||||
const unsigned struct_kernel_stat_sz = 80;
|
||||
const unsigned struct_kernel_stat64_sz = 136;
|
||||
# endif
|
||||
struct __sanitizer_perf_event_attr {
|
||||
unsigned type;
|
||||
@@ -323,7 +332,7 @@ struct __sanitizer_iovec {
|
||||
usize iov_len;
|
||||
};
|
||||
|
||||
# if !SANITIZER_ANDROID
|
||||
# if !SANITIZER_ANDROID && !SANITIZER_AIX
|
||||
struct __sanitizer_ifaddrs {
|
||||
struct __sanitizer_ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
@@ -337,7 +346,7 @@ struct __sanitizer_ifaddrs {
|
||||
void *ifa_dstaddr; // (struct sockaddr *)
|
||||
void *ifa_data;
|
||||
};
|
||||
# endif // !SANITIZER_ANDROID
|
||||
# endif // !SANITIZER_ANDROID && !SANITIZER_AIX
|
||||
|
||||
# if SANITIZER_APPLE
|
||||
typedef unsigned long __sanitizer_pthread_key_t;
|
||||
@@ -345,7 +354,7 @@ typedef unsigned long __sanitizer_pthread_key_t;
|
||||
typedef unsigned __sanitizer_pthread_key_t;
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
# if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_AIX
|
||||
|
||||
struct __sanitizer_XDR {
|
||||
int x_op;
|
||||
@@ -440,12 +449,14 @@ struct __sanitizer_tm {
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
# if SANITIZER_HAIKU
|
||||
# if !SANITIZER_AIX
|
||||
# if SANITIZER_HAIKU
|
||||
int tm_gmtoff;
|
||||
# else
|
||||
long int tm_gmtoff;
|
||||
# endif
|
||||
const char *tm_zone;
|
||||
# endif
|
||||
};
|
||||
|
||||
# if SANITIZER_LINUX
|
||||
@@ -513,11 +524,19 @@ struct __sanitizer_msghdr {
|
||||
struct __sanitizer_iovec *msg_iov;
|
||||
uptr msg_iovlen;
|
||||
void *msg_control;
|
||||
# if !SANITIZER_AIX
|
||||
uptr msg_controllen;
|
||||
# else
|
||||
unsigned msg_controllen;
|
||||
# endif
|
||||
int msg_flags;
|
||||
};
|
||||
struct __sanitizer_cmsghdr {
|
||||
# if !SANITIZER_AIX
|
||||
uptr cmsg_len;
|
||||
# else
|
||||
unsigned cmsg_len;
|
||||
# endif
|
||||
int cmsg_level;
|
||||
int cmsg_type;
|
||||
};
|
||||
@@ -554,10 +573,23 @@ struct __sanitizer_dirent {
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
# elif defined(__alpha__)
|
||||
struct __sanitizer_dirent {
|
||||
unsigned int d_ino; // ino_t is 32-bit on Alpha
|
||||
int __pad; // explicit padding before d_off
|
||||
unsigned long d_off;
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
# else
|
||||
struct __sanitizer_dirent {
|
||||
# if SANITIZER_AIX
|
||||
uptr d_offset;
|
||||
uptr d_ino;
|
||||
# else
|
||||
uptr d_ino;
|
||||
uptr d_off;
|
||||
# endif
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
@@ -573,7 +605,7 @@ struct __sanitizer_dirent64 {
|
||||
extern unsigned struct_sock_fprog_sz;
|
||||
# endif
|
||||
|
||||
# if SANITIZER_HAIKU
|
||||
# if SANITIZER_HAIKU || SANITIZER_AIX
|
||||
typedef int __sanitizer_clock_t;
|
||||
# elif defined(__x86_64__) && !defined(_LP64)
|
||||
typedef long long __sanitizer_clock_t;
|
||||
@@ -581,8 +613,10 @@ typedef long long __sanitizer_clock_t;
|
||||
typedef long __sanitizer_clock_t;
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LINUX || SANITIZER_HAIKU
|
||||
# if SANITIZER_LINUX || SANITIZER_HAIKU || SANITIZER_AIX
|
||||
typedef int __sanitizer_clockid_t;
|
||||
# endif
|
||||
# if SANITIZER_LINUX || SANITIZER_HAIKU
|
||||
typedef unsigned long long __sanitizer_eventfd_t;
|
||||
# endif
|
||||
|
||||
@@ -637,6 +671,14 @@ struct __sanitizer_sigset_t {
|
||||
// The size is determined by looking at sizeof of real sigset_t on linux.
|
||||
uptr val[128 / sizeof(uptr)];
|
||||
};
|
||||
# elif SANITIZER_AIX
|
||||
struct __sanitizer_sigset_t {
|
||||
# if SANITIZER_WORDSIZE == 64
|
||||
uptr val[4];
|
||||
# else
|
||||
uptr val[2];
|
||||
# endif
|
||||
};
|
||||
# endif
|
||||
|
||||
struct __sanitizer_siginfo_pad {
|
||||
@@ -741,7 +783,7 @@ struct __sanitizer_sigaction {
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
# if SANITIZER_LINUX || SANITIZER_HAIKU
|
||||
# if (SANITIZER_LINUX || SANITIZER_HAIKU) && !defined(__alpha__)
|
||||
void (*sa_restorer)();
|
||||
# endif
|
||||
# if defined(__mips__) && (SANITIZER_WORDSIZE == 32) && !SANITIZER_MUSL
|
||||
@@ -828,8 +870,12 @@ struct __sanitizer_addrinfo {
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
# if SANITIZER_ANDROID || SANITIZER_APPLE || SANITIZER_HAIKU
|
||||
# if SANITIZER_ANDROID || SANITIZER_APPLE || SANITIZER_HAIKU || SANITIZER_AIX
|
||||
# if SANITIZER_AIX // AIX ai_addrlen type is size_t
|
||||
uptr ai_addrlen;
|
||||
# else
|
||||
unsigned ai_addrlen;
|
||||
# endif
|
||||
char *ai_canonname;
|
||||
void *ai_addr;
|
||||
# else // LINUX
|
||||
@@ -838,6 +884,9 @@ struct __sanitizer_addrinfo {
|
||||
char *ai_canonname;
|
||||
# endif
|
||||
struct __sanitizer_addrinfo *ai_next;
|
||||
# if SANITIZER_AIX
|
||||
int ai_eflags;
|
||||
# endif
|
||||
};
|
||||
|
||||
struct __sanitizer_hostent {
|
||||
@@ -854,7 +903,7 @@ struct __sanitizer_pollfd {
|
||||
short revents;
|
||||
};
|
||||
|
||||
# if SANITIZER_ANDROID || SANITIZER_APPLE
|
||||
# if SANITIZER_ANDROID || SANITIZER_APPLE || SANITIZER_AIX
|
||||
typedef unsigned __sanitizer_nfds_t;
|
||||
# else
|
||||
typedef unsigned long __sanitizer_nfds_t;
|
||||
@@ -892,6 +941,10 @@ struct __sanitizer_wordexp_t {
|
||||
uptr we_wordc;
|
||||
char **we_wordv;
|
||||
uptr we_offs;
|
||||
# if SANITIZER_AIX
|
||||
int we_sflags;
|
||||
uptr we_soffs;
|
||||
# endif
|
||||
};
|
||||
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
@@ -1023,7 +1076,7 @@ struct __sanitizer_cookie_io_functions_t {
|
||||
# define IOC_NRBITS 8
|
||||
# define IOC_TYPEBITS 8
|
||||
# if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__) || \
|
||||
defined(__sparc__)
|
||||
defined(__sparc__) || defined(__alpha__)
|
||||
# define IOC_SIZEBITS 13
|
||||
# define IOC_DIRBITS 3
|
||||
# define IOC_NONE 1U
|
||||
@@ -1193,7 +1246,9 @@ extern unsigned IOCTL_TIOCMGET;
|
||||
extern unsigned IOCTL_TIOCMSET;
|
||||
extern unsigned IOCTL_TIOCNXCL;
|
||||
extern unsigned IOCTL_TIOCOUTQ;
|
||||
# if !SANITIZER_AIX
|
||||
extern unsigned IOCTL_TIOCSCTTY;
|
||||
# endif
|
||||
extern unsigned IOCTL_TIOCSPGRP;
|
||||
extern unsigned IOCTL_TIOCSWINSZ;
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
@@ -1593,6 +1648,7 @@ extern const int si_SEGV_ACCERR;
|
||||
typedef void *__sanitizer_timer_t;
|
||||
# endif
|
||||
|
||||
#endif // SANITIZER_LINUX || SANITIZER_APPLE || SANITIZER_HAIKU
|
||||
#endif // SANITIZER_LINUX || SANITIZER_APPLE || SANITIZER_HAIKU ||
|
||||
// SANITIZER_AIX
|
||||
|
||||
#endif
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
|
||||
+9
-7
@@ -27,12 +27,13 @@
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
# if SANITIZER_FREEBSD || SANITIZER_AIX
|
||||
// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
|
||||
// that, it was never implemented. So just define it to zero.
|
||||
#undef MAP_NORESERVE
|
||||
#define MAP_NORESERVE 0
|
||||
#endif
|
||||
// Similarly, AIX does not define MAP_NORESERVE.
|
||||
# undef MAP_NORESERVE
|
||||
# define MAP_NORESERVE 0
|
||||
# endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
@@ -357,9 +358,10 @@ int GetNamedMappingFd(const char *name, uptr size, int *flags) {
|
||||
if (!common_flags()->decorate_proc_maps || !name)
|
||||
return -1;
|
||||
char shmname[200];
|
||||
CHECK(internal_strlen(name) < sizeof(shmname) - 10);
|
||||
internal_snprintf(shmname, sizeof(shmname), "/dev/shm/%zu [%s]",
|
||||
internal_getpid(), name);
|
||||
int len =
|
||||
internal_snprintf(shmname, sizeof(shmname), "/dev/shm/%zu.%llu [%s]",
|
||||
internal_getpid(), GetTid(), name);
|
||||
CHECK_LT(len, sizeof(shmname));
|
||||
int o_cloexec = 0;
|
||||
#if defined(O_CLOEXEC)
|
||||
o_cloexec = O_CLOEXEC;
|
||||
|
||||
+2
-2
@@ -28,9 +28,9 @@ namespace __sanitizer {
|
||||
// Don't use directly, use __sanitizer::OpenFile() instead.
|
||||
uptr internal_open(const char *filename, int flags);
|
||||
uptr internal_open(const char *filename, int flags, u32 mode);
|
||||
# if SANITIZER_FREEBSD
|
||||
// Closes all file descriptors from lowfd to highfd (inclusive).
|
||||
// Returns 0 on success or non-zero if not supported on this platform.
|
||||
uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags);
|
||||
# endif
|
||||
uptr internal_close(fd_t fd);
|
||||
|
||||
uptr internal_read(fd_t fd, void *buf, uptr count);
|
||||
|
||||
+12
-9
@@ -188,12 +188,13 @@ static uptr GetAltStackSize() {
|
||||
return SIGSTKSZ * 4;
|
||||
}
|
||||
|
||||
void SetAlternateSignalStack() {
|
||||
void* SetAlternateSignalStack() {
|
||||
stack_t altstack, oldstack;
|
||||
CHECK_EQ(0, sigaltstack(nullptr, &oldstack));
|
||||
// If the alternate stack is already in place, do nothing.
|
||||
// Android always sets an alternate stack, but it's too small for us.
|
||||
if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;
|
||||
if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE))
|
||||
return nullptr;
|
||||
// TODO(glider): the mapped stack should have the MAP_STACK flag in the
|
||||
// future. It is not required by man 2 sigaltstack now (they're using
|
||||
// malloc()).
|
||||
@@ -201,15 +202,18 @@ void SetAlternateSignalStack() {
|
||||
altstack.ss_sp = (char *)MmapOrDie(altstack.ss_size, __func__);
|
||||
altstack.ss_flags = 0;
|
||||
CHECK_EQ(0, sigaltstack(&altstack, nullptr));
|
||||
return altstack.ss_sp;
|
||||
}
|
||||
|
||||
void UnsetAlternateSignalStack() {
|
||||
void UnsetAlternateSignalStack(void* altstack_base) {
|
||||
stack_t altstack, oldstack;
|
||||
altstack.ss_sp = nullptr;
|
||||
altstack.ss_flags = SS_DISABLE;
|
||||
altstack.ss_size = GetAltStackSize(); // Some sane value required on Darwin.
|
||||
CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
|
||||
UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
|
||||
if (altstack_base && altstack_base == oldstack.ss_sp) {
|
||||
UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSignalHandlerFromSanitizer(int signum) {
|
||||
@@ -562,11 +566,10 @@ pid_t StartSubprocess(const char *program, const char *const argv[],
|
||||
internal_close(stderr_fd);
|
||||
}
|
||||
|
||||
# if SANITIZER_FREEBSD
|
||||
internal_close_range(3, ~static_cast<fd_t>(0), 0);
|
||||
# else
|
||||
for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd);
|
||||
# endif
|
||||
// Close all fds except stdin/stdout/stderr before exec.
|
||||
// Fallback to the loop if close_range is not supported.
|
||||
if (internal_close_range(3, ~static_cast<fd_t>(0), 0) != 0)
|
||||
for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd);
|
||||
|
||||
internal_execve(program, const_cast<char **>(&argv[0]),
|
||||
const_cast<char *const *>(envp));
|
||||
|
||||
+17
-5
@@ -17,11 +17,23 @@
|
||||
// The asm hack only works with GCC and Clang.
|
||||
# if !defined(_WIN32) && !defined(_AIX) && !defined(__APPLE__)
|
||||
|
||||
asm(R"(
|
||||
.set memcpy, __sanitizer_internal_memcpy
|
||||
.set memmove, __sanitizer_internal_memmove
|
||||
.set memset, __sanitizer_internal_memset
|
||||
)");
|
||||
# if defined(__hexagon__)
|
||||
|
||||
# define SANITIZER_REDEFINE_BUILTIN_ASM(name) \
|
||||
asm(".set " #name ", __sanitizer_internal_" #name)
|
||||
|
||||
# else
|
||||
|
||||
# define SANITIZER_REDEFINE_BUILTIN_ASM(name) \
|
||||
asm(#name " = __sanitizer_internal_" #name)
|
||||
|
||||
# endif
|
||||
|
||||
SANITIZER_REDEFINE_BUILTIN_ASM(memcpy);
|
||||
SANITIZER_REDEFINE_BUILTIN_ASM(memmove);
|
||||
SANITIZER_REDEFINE_BUILTIN_ASM(memset);
|
||||
|
||||
# undef SANITIZER_REDEFINE_BUILTIN_ASM
|
||||
|
||||
# if defined(__cplusplus) && \
|
||||
!defined(SANITIZER_COMMON_REDEFINE_BUILTINS_IN_STD)
|
||||
|
||||
@@ -102,6 +102,10 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
|
||||
return _REAL64(open)(filename, flags, mode);
|
||||
}
|
||||
|
||||
uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
|
||||
return -1; // Not supported.
|
||||
}
|
||||
|
||||
DECLARE__REAL_AND_INTERNAL(uptr, read, fd_t fd, void *buf, uptr count) {
|
||||
return _REAL(read)(fd, buf, count);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
#if SANITIZER_LINUX && \
|
||||
(defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \
|
||||
defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \
|
||||
defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64)
|
||||
defined(__arm__) || defined(__hexagon__) || SANITIZER_RISCV64 || \
|
||||
SANITIZER_LOONGARCH64)
|
||||
|
||||
#include "sanitizer_stoptheworld.h"
|
||||
|
||||
@@ -32,8 +33,8 @@
|
||||
#include <sys/uio.h> // for iovec
|
||||
#include <elf.h> // for NT_PRSTATUS
|
||||
#if (defined(__aarch64__) || defined(__powerpc64__) || \
|
||||
SANITIZER_RISCV64 || SANITIZER_LOONGARCH64) && \
|
||||
!SANITIZER_ANDROID
|
||||
defined(__hexagon__) || SANITIZER_RISCV64 || \
|
||||
SANITIZER_LOONGARCH64) && !SANITIZER_ANDROID
|
||||
// GLIBC 2.20+ sys/user does not include asm/ptrace.h
|
||||
# include <asm/ptrace.h>
|
||||
#endif
|
||||
@@ -613,6 +614,13 @@ typedef _user_regs_struct regs_struct;
|
||||
static constexpr uptr kExtraRegs[] = {0};
|
||||
#define ARCH_IOVEC_FOR_GETREGSET
|
||||
|
||||
#elif defined(__hexagon__)
|
||||
#include <asm/user.h>
|
||||
typedef struct user_regs_struct regs_struct;
|
||||
#define REG_SP r29
|
||||
static constexpr uptr kExtraRegs[] = {0};
|
||||
#define ARCH_IOVEC_FOR_GETREGSET
|
||||
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif // SANITIZER_ANDROID && defined(__arm__)
|
||||
|
||||
@@ -475,6 +475,13 @@ static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
|
||||
return new (*allocator) Addr2LinePool(found_path, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
# if SANITIZER_APPLE
|
||||
Report(
|
||||
"WARN: No external symbolizers found. Symbols may be missing or "
|
||||
"unreliable.\n");
|
||||
Report("HINT: Is PATH set? Does sandbox allow file-read of /usr/bin/atos?\n");
|
||||
# endif
|
||||
return nullptr;
|
||||
# endif // SANITIZER_DISABLE_SYMBOLIZER_PATH_SEARCH
|
||||
}
|
||||
@@ -509,13 +516,6 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
|
||||
}
|
||||
|
||||
# if SANITIZER_APPLE
|
||||
if (list->empty()) {
|
||||
Report(
|
||||
"WARN: No external symbolizers found. Symbols may be missing or "
|
||||
"unreliable.\n");
|
||||
Report(
|
||||
"HINT: Is PATH set? Does sandbox allow file-read of /usr/bin/atos?\n");
|
||||
}
|
||||
VReport(2, "Using dladdr symbolizer.\n");
|
||||
list->push_back(new (*allocator) DlAddrSymbolizer());
|
||||
# endif // SANITIZER_APPLE
|
||||
|
||||
@@ -184,7 +184,7 @@ static void MaybeReportNonExecRegion(uptr pc) {
|
||||
MemoryMappedSegment segment;
|
||||
while (proc_maps.Next(&segment)) {
|
||||
if (pc >= segment.start && pc < segment.end && !segment.IsExecutable())
|
||||
Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n");
|
||||
Report("HINT: PC is at a non-executable region. Maybe a wild jump?\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -254,7 +254,7 @@ static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
|
||||
(void *)sig.bp, (void *)sig.sp, tid);
|
||||
Printf("%s", d.Default());
|
||||
if (sig.pc < GetPageSizeCached())
|
||||
Report("Hint: pc points to the zero page.\n");
|
||||
Report("HINT: pc points to the zero page.\n");
|
||||
if (sig.is_memory_access) {
|
||||
const char *access_type =
|
||||
sig.write_flag == SignalContext::Write
|
||||
@@ -262,11 +262,12 @@ static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
|
||||
: (sig.write_flag == SignalContext::Read ? "READ" : "UNKNOWN");
|
||||
Report("The signal is caused by a %s memory access.\n", access_type);
|
||||
if (!sig.is_true_faulting_addr)
|
||||
Report("Hint: this fault was caused by a dereference of a high value "
|
||||
"address (see register values below). Disassemble the provided "
|
||||
"pc to learn which register was used.\n");
|
||||
Report(
|
||||
"HINT: this fault was caused by a dereference of a high value "
|
||||
"address (see register values below). Disassemble the provided "
|
||||
"pc to learn which register was used.\n");
|
||||
else if (sig.addr < GetPageSizeCached())
|
||||
Report("Hint: address points to the zero page.\n");
|
||||
Report("HINT: address points to the zero page.\n");
|
||||
}
|
||||
MaybeReportNonExecRegion(sig.pc);
|
||||
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
|
||||
|
||||
+43
-6
@@ -43,10 +43,47 @@ void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
|
||||
trace_buffer[0] = pc;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wframe-larger-than="
|
||||
#endif
|
||||
PVOID CALLBACK FallbackFunctionTableAccess(HANDLE hProcess,
|
||||
DWORD64 dwAddrBase) {
|
||||
// First try DbgHelp's function.
|
||||
if (PVOID pResult =
|
||||
__sanitizer::SymFunctionTableAccess64(hProcess, dwAddrBase)) {
|
||||
return pResult;
|
||||
}
|
||||
|
||||
// Fall back to RtlLookupFunctionEntry for dynamic code.
|
||||
// Function registered with RtlAddFunctionTable is not necessarily registered
|
||||
// with DbgHelp, so this is required to cover some edge cases (e.g. JIT
|
||||
// compilers can use Rtl* functions).
|
||||
# if SANITIZER_WINDOWS64
|
||||
DWORD64 dw64ImageBase = 0;
|
||||
return RtlLookupFunctionEntry(dwAddrBase, &dw64ImageBase, nullptr);
|
||||
# else
|
||||
return nullptr;
|
||||
# endif
|
||||
}
|
||||
|
||||
DWORD64 CALLBACK FallbackGetModuleBase(HANDLE hProcess, DWORD64 dwAddr) {
|
||||
if (DWORD64 dwResult = __sanitizer::SymGetModuleBase64(hProcess, dwAddr)) {
|
||||
return dwResult;
|
||||
}
|
||||
|
||||
// Both GetModuleBase and FunctionTableAccess must provide this fallback,
|
||||
// otherwise dynamic functions won't be properly unwound.
|
||||
# if SANITIZER_WINDOWS64
|
||||
DWORD64 dw64ImageBase = 0;
|
||||
if (RtlLookupFunctionEntry(dwAddr, &dw64ImageBase, nullptr)) {
|
||||
return dw64ImageBase;
|
||||
}
|
||||
# endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wframe-larger-than="
|
||||
# endif
|
||||
void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
|
||||
CHECK(context);
|
||||
CHECK_GE(max_depth, 2);
|
||||
@@ -91,8 +128,8 @@ void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
|
||||
stack_frame.AddrFrame.Mode = AddrModeFlat;
|
||||
stack_frame.AddrStack.Mode = AddrModeFlat;
|
||||
while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
|
||||
&stack_frame, &ctx, NULL, SymFunctionTableAccess64,
|
||||
SymGetModuleBase64, NULL) &&
|
||||
&stack_frame, &ctx, NULL, FallbackFunctionTableAccess,
|
||||
FallbackGetModuleBase, NULL) &&
|
||||
size < Min(max_depth, kStackTraceMax)) {
|
||||
trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
|
||||
}
|
||||
|
||||
+4
-3
@@ -913,11 +913,12 @@ void ReportFile::Write(const char *buffer, uptr length) {
|
||||
}
|
||||
}
|
||||
|
||||
void SetAlternateSignalStack() {
|
||||
void* SetAlternateSignalStack() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UnsetAlternateSignalStack() {
|
||||
void UnsetAlternateSignalStack(void* altstack_base) {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
@@ -1222,7 +1223,7 @@ int WaitForProcess(pid_t pid) { return -1; }
|
||||
// FIXME implement on this platform.
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
|
||||
|
||||
void CheckNoDeepBind(const char *filename, int flag) {
|
||||
void OnDlOpen(const char* filename, int flag) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
Vendored
+433
@@ -0,0 +1,433 @@
|
||||
//===-- tsan_adaptive_delay.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer (TSan), a race detector.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "tsan_adaptive_delay.h"
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_errno_codes.h"
|
||||
#include "tsan_interface.h"
|
||||
#include "tsan_rtl.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
namespace {
|
||||
|
||||
// =============================================================================
|
||||
// DelaySpec: Represents a delay configuration parsed from flag strings
|
||||
// =============================================================================
|
||||
//
|
||||
// Delay can be specified as:
|
||||
// - "spin=N" : Spin for up to N cycles (very short delays)
|
||||
// - "yield" : Call sched_yield() once
|
||||
// - "sleep_us=N" : Sleep for up to N microseconds
|
||||
|
||||
enum class DelayType { Spin, Yield, SleepUs };
|
||||
|
||||
struct DelaySpec {
|
||||
DelayType type;
|
||||
int value; // spin cycles or sleep_us value; ignored for yield
|
||||
|
||||
// Both estimates below are used internally as a very rough estimate for
|
||||
// delay overhead calculation, to cap the overall delay to the
|
||||
// adaptive_delay_aggressiveness option. They're not intended to be 100%
|
||||
// accurate on any or all architectures/operating systems, or for use in any
|
||||
// other contexts.
|
||||
//
|
||||
// Estimated nanoseconds per spin cycle (volatile loop iteration).
|
||||
static constexpr u64 kNsPerSpinCycle = 1;
|
||||
// Estimated nanoseconds for a yield (context switch overhead)
|
||||
static constexpr u64 kNsPerYield = 500;
|
||||
|
||||
static DelaySpec Parse(const char* str) {
|
||||
DelaySpec spec;
|
||||
if (internal_strncmp(str, "spin=", 5) == 0) {
|
||||
spec.type = DelayType::Spin;
|
||||
spec.value = internal_atoll(str + 5);
|
||||
if (spec.value <= 0 || spec.value > 10000) {
|
||||
Printf(
|
||||
"FATAL: Invalid TSAN_OPTIONS spin value '%s'; value must be "
|
||||
"between 1 and 10000\n",
|
||||
str);
|
||||
Die();
|
||||
}
|
||||
} else if (internal_strcmp(str, "yield") == 0) {
|
||||
spec.type = DelayType::Yield;
|
||||
spec.value = 0;
|
||||
} else if (internal_strncmp(str, "sleep_us=", 9) == 0) {
|
||||
spec.type = DelayType::SleepUs;
|
||||
spec.value = internal_atoll(str + 9);
|
||||
if (spec.value <= 0) {
|
||||
Printf(
|
||||
"FATAL: Invalid TSAN_OPTIONS sleep_us value '%s'; value must be a "
|
||||
"positive integer\n",
|
||||
str);
|
||||
Die();
|
||||
}
|
||||
} else {
|
||||
Printf("FATAL: Unrecognized delay spec '%s', check TSAN_OPTIONS\n", str);
|
||||
Die();
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
const char* TypeName() const {
|
||||
switch (type) {
|
||||
case DelayType::Spin:
|
||||
return "spin";
|
||||
case DelayType::Yield:
|
||||
return "yield";
|
||||
case DelayType::SleepUs:
|
||||
return "sleep_us";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// =============================================================================
|
||||
// AdaptiveDelayImpl: Time-budget aware delay injection for race exposure
|
||||
// =============================================================================
|
||||
//
|
||||
// This implementation injects delays to expose data races while maintaining a
|
||||
// configurable overhead target. It uses several strategies:
|
||||
//
|
||||
// 1. Time-Budget Controller: Tracks cumulative delays vs wall-clock time
|
||||
// and adjusts delay probability to maintain target overhead.
|
||||
//
|
||||
// 2. Tiered Delays: Different delay strategies for different op types:
|
||||
// - Relaxed atomics: Very rare sampling, tiny spin delays
|
||||
// - Sync atomics (acq/rel/seq_cst): Moderate sampling, small usleep
|
||||
// - Mutex/CV ops: Higher sampling, larger delays
|
||||
// - Thread create/join: Always delay (rare but high value)
|
||||
//
|
||||
// 3. Address-based Sampling: Exponential backoff per address to avoid
|
||||
// repeatedly delaying hot atomics.
|
||||
|
||||
struct AdaptiveDelayImpl {
|
||||
ALWAYS_INLINE static AdaptiveDelayState* TLS() {
|
||||
return &cur_thread()->adaptive_delay_state;
|
||||
}
|
||||
ALWAYS_INLINE static unsigned int* GetRandomSeed() {
|
||||
return &TLS()->tls_random_seed_;
|
||||
}
|
||||
ALWAYS_INLINE static void SetRandomSeed(unsigned int seed) {
|
||||
TLS()->tls_random_seed_ = seed;
|
||||
}
|
||||
|
||||
// The public facing option is adaptive_delay_aggressiveness, which is an
|
||||
// opaque value for the user to tune the amount of delay injected into the
|
||||
// program. Internally, the implementation maps the aggressiveness to a target
|
||||
// percent delay for the overall program runtime. It's not easy to implement
|
||||
// a true wall clock delay target (e.g., 25% program wall time slowdown)
|
||||
// because 1) spin loops and yield are hard to calculate actual wall time
|
||||
// slowness and 2) usleep(N) is often slower than advertised. Thus, we keep
|
||||
// the user facing parameter opaque to not under deliver on a promise of
|
||||
// percent wall time slowdown.
|
||||
struct TimeBudget {
|
||||
int target_overhead_pct_;
|
||||
Percent target_low_;
|
||||
Percent target_high_;
|
||||
|
||||
void Init(int target_pct) {
|
||||
target_overhead_pct_ = target_pct;
|
||||
target_low_ = Percent::FromPct(
|
||||
target_overhead_pct_ >= 5 ? target_overhead_pct_ - 5 : 0);
|
||||
target_high_ = Percent::FromPct(target_overhead_pct_ + 5);
|
||||
}
|
||||
|
||||
static constexpr u64 BucketDurationNs = 30'000'000'000ULL;
|
||||
|
||||
void RecordDelay(u64 delay_ns) {
|
||||
u64 now = NanoTime();
|
||||
u64 elapsed_ns = now - TLS()->bucket_start_ns_;
|
||||
|
||||
if (elapsed_ns >= BucketDurationNs) {
|
||||
// Shift: old bucket is discarded, new becomes old, start fresh new
|
||||
TLS()->delay_buckets_ns_[0] = TLS()->delay_buckets_ns_[1];
|
||||
TLS()->delay_buckets_ns_[1] = 0;
|
||||
TLS()->bucket_start_ns_ = now;
|
||||
TLS()->bucket0_window_ns = BucketDurationNs;
|
||||
}
|
||||
|
||||
TLS()->delay_buckets_ns_[1] += delay_ns;
|
||||
}
|
||||
|
||||
Percent GetOverheadPercent() {
|
||||
u64 now = NanoTime();
|
||||
u64 elapsed_ns = now - TLS()->bucket_start_ns_;
|
||||
|
||||
// Need at least 1ms to calculate
|
||||
if (elapsed_ns < 1'000'000ULL)
|
||||
return Percent::FromPct(0);
|
||||
|
||||
if (elapsed_ns > BucketDurationNs * 2) {
|
||||
// Both buckets are stale
|
||||
return Percent::FromPct(0);
|
||||
} else if (elapsed_ns > BucketDurationNs) {
|
||||
// bucket[0] is stale, use only bucket[1] (current bucket)
|
||||
u64 total_delay_ns = TLS()->delay_buckets_ns_[1];
|
||||
return Percent::FromRatio(total_delay_ns, elapsed_ns);
|
||||
} else {
|
||||
u64 total_delay_ns =
|
||||
TLS()->delay_buckets_ns_[0] + TLS()->delay_buckets_ns_[1];
|
||||
u64 window_ns = TLS()->bucket0_window_ns + elapsed_ns;
|
||||
return Percent::FromRatio(total_delay_ns, window_ns);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldDelay() {
|
||||
Percent ratio = GetOverheadPercent();
|
||||
|
||||
if (ratio < target_low_)
|
||||
return true;
|
||||
if (ratio > target_high_)
|
||||
return false;
|
||||
|
||||
// Linear interpolation: at target_low -> 100%, at target_high -> 0%
|
||||
Percent prob = (target_high_ - ratio) / (target_high_ - target_low_);
|
||||
return prob.RandomCheck(GetRandomSeed());
|
||||
}
|
||||
};
|
||||
|
||||
// Address Sampler with Exponential Backoff
|
||||
struct AddressSampler {
|
||||
static constexpr u64 TABLE_SIZE = 2048;
|
||||
struct Entry {
|
||||
atomic_uintptr_t addr_;
|
||||
atomic_uint32_t count_;
|
||||
};
|
||||
Entry table_[TABLE_SIZE];
|
||||
static constexpr u32 ExponentialBackoffCap = 64;
|
||||
|
||||
void Init() {
|
||||
for (u64 i = 0; i < TABLE_SIZE; ++i) {
|
||||
atomic_store(&table_[i].addr_, 0, memory_order_relaxed);
|
||||
atomic_store(&table_[i].count_, 0, memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE u64 splitmix64(u64 x) {
|
||||
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
|
||||
x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
|
||||
x = x ^ (x >> 31);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Uses exponential backoff: delay on 1st, 2nd, 4th, 8th, 16th, ...
|
||||
bool ShouldDelayAddr(uptr addr) {
|
||||
u64 idx = splitmix64(addr >> 3) & (TABLE_SIZE - 1);
|
||||
Entry& e = table_[idx];
|
||||
|
||||
// This function is not thread safe.
|
||||
// If two threads access the same hashed entry in parallel,
|
||||
// worst case, we may end up returning true too often. This is
|
||||
// acceptable...instead of full locking.
|
||||
|
||||
uptr stored_addr = atomic_load(&e.addr_, memory_order_relaxed);
|
||||
if (stored_addr != addr) {
|
||||
// Hash Collision - reset
|
||||
atomic_store(&e.addr_, addr, memory_order_relaxed);
|
||||
atomic_store(&e.count_, 1, memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 count = atomic_fetch_add(&e.count_, 1, memory_order_relaxed) + 1;
|
||||
|
||||
if ((count & (count - 1)) == 0 && count <= ExponentialBackoffCap)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
TimeBudget budget_;
|
||||
AddressSampler sampler_;
|
||||
|
||||
int relaxed_sample_rate_;
|
||||
int sync_atomic_sample_rate_;
|
||||
int mutex_sample_rate_;
|
||||
DelaySpec atomic_delay_;
|
||||
DelaySpec sync_delay_;
|
||||
|
||||
void Init() { InitTls(); }
|
||||
|
||||
void InitTls() {
|
||||
TLS()->bucket_start_ns_ = NanoTime();
|
||||
TLS()->delay_buckets_ns_[0] = 0;
|
||||
TLS()->delay_buckets_ns_[1] = 0;
|
||||
TLS()->bucket0_window_ns = 0;
|
||||
|
||||
SetRandomSeed(NanoTime());
|
||||
TLS()->tls_initialized_ = true;
|
||||
}
|
||||
|
||||
bool IsTlsInitialized() const { return TLS()->tls_initialized_; }
|
||||
|
||||
AdaptiveDelayImpl() {
|
||||
relaxed_sample_rate_ = flags()->adaptive_delay_relaxed_sample_rate;
|
||||
sync_atomic_sample_rate_ = flags()->adaptive_delay_sync_atomic_sample_rate;
|
||||
mutex_sample_rate_ = flags()->adaptive_delay_mutex_sample_rate;
|
||||
atomic_delay_ = DelaySpec::Parse(flags()->adaptive_delay_max_atomic);
|
||||
sync_delay_ = DelaySpec::Parse(flags()->adaptive_delay_max_sync);
|
||||
|
||||
int delay_aggressiveness = flags()->adaptive_delay_aggressiveness;
|
||||
if (delay_aggressiveness < 1)
|
||||
delay_aggressiveness = 1;
|
||||
|
||||
budget_.Init(delay_aggressiveness);
|
||||
sampler_.Init();
|
||||
|
||||
VPrintf(1, "INFO: ThreadSanitizer AdaptiveDelay initialized\n");
|
||||
VPrintf(1, " Delay aggressiveness: %d\n", delay_aggressiveness);
|
||||
VPrintf(1, " Relaxed atomic sample rate: 1/%d\n", relaxed_sample_rate_);
|
||||
VPrintf(1, " Sync atomic sample rate: 1/%d\n", sync_atomic_sample_rate_);
|
||||
VPrintf(1, " Mutex sample rate: 1/%d\n", mutex_sample_rate_);
|
||||
VPrintf(1, " Atomic delay: %s=%d\n", atomic_delay_.TypeName(),
|
||||
atomic_delay_.value);
|
||||
VPrintf(1, " Sync delay: %s=%d\n", sync_delay_.TypeName(),
|
||||
sync_delay_.value);
|
||||
}
|
||||
|
||||
void DoSpinDelay(int iters) {
|
||||
volatile int v = 0;
|
||||
for (int i = 0; i < iters; ++i) v = i;
|
||||
(void)v;
|
||||
budget_.RecordDelay(iters * DelaySpec::kNsPerSpinCycle);
|
||||
}
|
||||
|
||||
void DoYieldDelay() {
|
||||
internal_sched_yield();
|
||||
budget_.RecordDelay(DelaySpec::kNsPerYield);
|
||||
}
|
||||
|
||||
void DoSleepUsDelay(int max_us) {
|
||||
// Use two Rand() calls to get full 32-bit range for larger sleep values
|
||||
u32 rnd = ((u32)Rand(GetRandomSeed()) << 16) | Rand(GetRandomSeed());
|
||||
int delay_us = 1 + (rnd % max_us);
|
||||
internal_usleep(delay_us);
|
||||
budget_.RecordDelay(delay_us * 1000ULL);
|
||||
}
|
||||
|
||||
void ExecuteDelay(const DelaySpec& spec) {
|
||||
switch (spec.type) {
|
||||
case DelayType::Spin: {
|
||||
int iters = 1 + (Rand(GetRandomSeed()) % spec.value);
|
||||
DoSpinDelay(iters);
|
||||
break;
|
||||
}
|
||||
case DelayType::Yield:
|
||||
DoYieldDelay();
|
||||
break;
|
||||
case DelayType::SleepUs:
|
||||
DoSleepUsDelay(spec.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AtomicRelaxedOpDelay() {
|
||||
if ((Rand(GetRandomSeed()) % relaxed_sample_rate_) != 0)
|
||||
return;
|
||||
if (!budget_.ShouldDelay())
|
||||
return;
|
||||
|
||||
int iters = 10 + (Rand(GetRandomSeed()) % 10);
|
||||
DoSpinDelay(iters);
|
||||
}
|
||||
|
||||
void AtomicSyncOpDelay(uptr* addr) {
|
||||
if ((Rand(GetRandomSeed()) % sync_atomic_sample_rate_) != 0)
|
||||
return;
|
||||
if (!budget_.ShouldDelay())
|
||||
return;
|
||||
|
||||
if (addr && !sampler_.ShouldDelayAddr(*addr))
|
||||
return;
|
||||
|
||||
ExecuteDelay(atomic_delay_);
|
||||
}
|
||||
|
||||
void AtomicOpFence(int mo) {
|
||||
CHECK(IsTlsInitialized());
|
||||
|
||||
if (mo < mo_acquire)
|
||||
AtomicRelaxedOpDelay();
|
||||
else
|
||||
AtomicSyncOpDelay(nullptr);
|
||||
}
|
||||
|
||||
void AtomicOpAddr(uptr addr, int mo) {
|
||||
CHECK(IsTlsInitialized());
|
||||
|
||||
if (mo < mo_acquire)
|
||||
AtomicRelaxedOpDelay();
|
||||
else
|
||||
AtomicSyncOpDelay(&addr);
|
||||
}
|
||||
|
||||
void UnsampledDelay() {
|
||||
CHECK(IsTlsInitialized());
|
||||
|
||||
if (!budget_.ShouldDelay())
|
||||
return;
|
||||
|
||||
ExecuteDelay(sync_delay_);
|
||||
}
|
||||
|
||||
void SyncOp() {
|
||||
CHECK(IsTlsInitialized());
|
||||
|
||||
if ((Rand(GetRandomSeed()) % mutex_sample_rate_) != 0)
|
||||
return;
|
||||
if (!budget_.ShouldDelay())
|
||||
return;
|
||||
|
||||
ExecuteDelay(sync_delay_);
|
||||
}
|
||||
|
||||
void BeforeChildThreadRuns() {
|
||||
InitTls();
|
||||
UnsampledDelay();
|
||||
}
|
||||
|
||||
void AfterThreadCreation() { UnsampledDelay(); }
|
||||
};
|
||||
|
||||
AdaptiveDelayImpl& GetImpl() {
|
||||
static AdaptiveDelayImpl impl;
|
||||
return impl;
|
||||
}
|
||||
|
||||
bool AdaptiveDelay::is_adaptive_delay_enabled;
|
||||
|
||||
void AdaptiveDelay::InitImpl() {
|
||||
AdaptiveDelay::is_adaptive_delay_enabled = flags()->enable_adaptive_delay;
|
||||
if (!AdaptiveDelay::is_adaptive_delay_enabled)
|
||||
return;
|
||||
|
||||
GetImpl().Init();
|
||||
}
|
||||
|
||||
void AdaptiveDelay::SyncOpImpl() { GetImpl().SyncOp(); }
|
||||
void AdaptiveDelay::AtomicOpFenceImpl(int mo) { GetImpl().AtomicOpFence(mo); }
|
||||
void AdaptiveDelay::AtomicOpAddrImpl(__sanitizer::uptr addr, int mo) {
|
||||
GetImpl().AtomicOpAddr(addr, mo);
|
||||
}
|
||||
void AdaptiveDelay::AfterThreadCreationImpl() {
|
||||
GetImpl().AfterThreadCreation();
|
||||
}
|
||||
void AdaptiveDelay::BeforeChildThreadRunsImpl() {
|
||||
GetImpl().BeforeChildThreadRuns();
|
||||
}
|
||||
|
||||
} // namespace __tsan
|
||||
Vendored
+173
@@ -0,0 +1,173 @@
|
||||
//===-- tsan_adaptive_delay.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer (TSan), a race detector.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef TSAN_ADAPTIVE_DELAY_H
|
||||
#define TSAN_ADAPTIVE_DELAY_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
// AdaptiveDelay injects delays at synchronization points, atomic operations,
|
||||
// and thread lifecycle events to increase the likelihood of exposing data
|
||||
// races. The delay injection is controlled by an approximate time budget to
|
||||
// maintain a configurable overhead target.
|
||||
//
|
||||
// SyncOp() delays non-atomic synchronization points (those with clear
|
||||
// happens-before relationships):
|
||||
// - Acquire operations like locking a mutex delays before the mutex is locked.
|
||||
// - Release operations like unlocking a mutex delays after the mutex is
|
||||
// unlocked
|
||||
// These are more likely to expose interesting (rare) thread interleavings.
|
||||
// For example, delaying a thread that unlocks a mutex from running to allow
|
||||
// newly woken thread to execute before the unlocking thread would normally
|
||||
// execute.
|
||||
//
|
||||
// TODO:
|
||||
// - Move the adaptive delay implementation into sanitizer_common so that
|
||||
// ASAN can also leverage it in pthread_* interceptors
|
||||
// - Integrate into other interceptors like libdispatch.
|
||||
struct AdaptiveDelay {
|
||||
ALWAYS_INLINE static void Init() { InitImpl(); }
|
||||
|
||||
ALWAYS_INLINE static void SyncOp() {
|
||||
if (!is_adaptive_delay_enabled)
|
||||
return;
|
||||
SyncOpImpl();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void AtomicOpFence(int mo) {
|
||||
if (!is_adaptive_delay_enabled)
|
||||
return;
|
||||
AtomicOpFenceImpl(mo);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void AtomicOpAddr(__sanitizer::uptr addr, int mo) {
|
||||
if (!is_adaptive_delay_enabled)
|
||||
return;
|
||||
AtomicOpAddrImpl(addr, mo);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void AfterThreadCreation() {
|
||||
if (!is_adaptive_delay_enabled)
|
||||
return;
|
||||
AfterThreadCreationImpl();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void BeforeChildThreadRuns() {
|
||||
if (!is_adaptive_delay_enabled)
|
||||
return;
|
||||
BeforeChildThreadRunsImpl();
|
||||
}
|
||||
|
||||
private:
|
||||
static void InitImpl();
|
||||
|
||||
static void SyncOpImpl();
|
||||
|
||||
static void AtomicOpFenceImpl(int mo);
|
||||
static void AtomicOpAddrImpl(__sanitizer::uptr addr, int mo);
|
||||
|
||||
static void AfterThreadCreationImpl();
|
||||
static void BeforeChildThreadRunsImpl();
|
||||
|
||||
static bool is_adaptive_delay_enabled;
|
||||
};
|
||||
|
||||
// The runtime defines cur_thread() to retrieve TLS thread state, and it
|
||||
// takes care of platform specific implementation details. The AdaptiveDelay
|
||||
// implementation stores per-thread data in this struct, which is embedded
|
||||
// in cur_thread().
|
||||
struct AdaptiveDelayState {
|
||||
// For the adaptive delay implementation
|
||||
// Sliding window delay tracking: 2 buckets of 30 seconds each
|
||||
u64 delay_buckets_ns_[2]; // [0] = older 30s, [1] = newer 30s
|
||||
u64 bucket_start_ns_; // When current bucket (index 1) started
|
||||
u64 bucket0_window_ns; // 0ns before the first bucket has rolled, and set to
|
||||
// the bucket window time after This handles the case
|
||||
// where, before the program has ran one bucket window
|
||||
// duration, we should not include the previous bucket
|
||||
// duration in the overhead percent calculation.
|
||||
unsigned int tls_random_seed_;
|
||||
bool tls_initialized_;
|
||||
};
|
||||
|
||||
// Fixed-point arithmetic type that mimics floating point operations
|
||||
class Percent {
|
||||
using u32 = __sanitizer::u32;
|
||||
using u64 = __sanitizer::u64;
|
||||
|
||||
u32 bp_{}; // basis points (0-10000 represents 0.0-1.0)
|
||||
bool is_valid_{};
|
||||
|
||||
static constexpr u32 kBasisPointsPerUnit = 10000;
|
||||
|
||||
Percent(u32 bp, bool is_valid) : bp_(bp), is_valid_(is_valid) {}
|
||||
|
||||
public:
|
||||
Percent() = default;
|
||||
Percent(const Percent&) = default;
|
||||
Percent& operator=(const Percent&) = default;
|
||||
Percent(Percent&&) = default;
|
||||
Percent& operator=(Percent&&) = default;
|
||||
|
||||
static Percent FromPct(u32 pct) { return Percent{pct * 100, true}; }
|
||||
static Percent FromRatio(u64 numerator, u64 denominator) {
|
||||
if (denominator == 0)
|
||||
return Percent{0, false};
|
||||
// Avoid overflow: scale down if needed
|
||||
if (numerator > UINT64_MAX / kBasisPointsPerUnit) {
|
||||
return Percent{(u32)((numerator / denominator) * kBasisPointsPerUnit),
|
||||
true};
|
||||
}
|
||||
return Percent{(u32)((numerator * kBasisPointsPerUnit) / denominator),
|
||||
true};
|
||||
}
|
||||
|
||||
bool IsValid() const { return is_valid_; }
|
||||
|
||||
// Returns true with probability equal to the percentage.
|
||||
bool RandomCheck(u32* seed) const {
|
||||
return (Rand(seed) % kBasisPointsPerUnit) < bp_;
|
||||
}
|
||||
|
||||
int GetPct() const { return bp_ / 100; }
|
||||
int GetBasisPoints() const { return bp_; }
|
||||
|
||||
bool operator==(const Percent& other) const { return bp_ == other.bp_; }
|
||||
bool operator!=(const Percent& other) const { return bp_ != other.bp_; }
|
||||
bool operator<(const Percent& other) const { return bp_ < other.bp_; }
|
||||
bool operator>(const Percent& other) const { return bp_ > other.bp_; }
|
||||
bool operator<=(const Percent& other) const { return bp_ <= other.bp_; }
|
||||
bool operator>=(const Percent& other) const { return bp_ >= other.bp_; }
|
||||
|
||||
Percent operator-(const Percent& other) const {
|
||||
if (!is_valid_ || !other.is_valid_)
|
||||
return Percent{0, false};
|
||||
if (bp_ < other.bp_)
|
||||
return Percent{0, false};
|
||||
return Percent{bp_ - other.bp_, true};
|
||||
}
|
||||
|
||||
Percent operator/(const Percent& other) const {
|
||||
if (!is_valid_ || !other.is_valid_)
|
||||
return Percent{0, false};
|
||||
if (other.bp_ == 0)
|
||||
return Percent{0, false};
|
||||
return Percent{(bp_ * kBasisPointsPerUnit) / other.bp_, true};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
#endif // TSAN_ADAPTIVE_DELAY_H
|
||||
Vendored
+1
-1
@@ -37,7 +37,7 @@ inline bool FlagHandler<LockDuringWriteSetting>::Parse(const char *value) {
|
||||
*t_ = kNoLockDuringWritesAllProcesses;
|
||||
return true;
|
||||
}
|
||||
Printf("ERROR: Invalid value for signal handler option: '%s'\n", value);
|
||||
Printf("ERROR: Invalid value for lock_during_write option: '%s'\n", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+27
@@ -92,3 +92,30 @@ TSAN_FLAG(LockDuringWriteSetting, lock_during_write, kLockDuringAllWrites,
|
||||
"\"disable_for_all_processes\" - don't lock during all writes in "
|
||||
"the current process and it's children processes.")
|
||||
#endif
|
||||
|
||||
TSAN_FLAG(bool, enable_adaptive_delay, false,
|
||||
"Enable adaptive delay injection to expose data races. When "
|
||||
"enabled, delays are strategically injected at synchronization "
|
||||
"points, atomic operations, and thread lifecycle events to increase "
|
||||
"the likelihood of exposing races while maintaining a configurable "
|
||||
"overhead budget.")
|
||||
|
||||
TSAN_FLAG(
|
||||
int, adaptive_delay_aggressiveness, 25,
|
||||
"Controls delay injection intensity for race detection. Higher values "
|
||||
"inject more delays to expose races. Suggested values: 10 (minimal delay), "
|
||||
"50 (moderate delay), 200 (aggressive). "
|
||||
"This is a tuning parameter; actual overhead varies by workload and "
|
||||
"platform.")
|
||||
TSAN_FLAG(int, adaptive_delay_relaxed_sample_rate, 10000,
|
||||
"Sample 1 in N relaxed atomic operations for delay")
|
||||
TSAN_FLAG(int, adaptive_delay_sync_atomic_sample_rate, 100,
|
||||
"Sample 1 in N acquire/release/seq_cst atomic operations for delay")
|
||||
TSAN_FLAG(int, adaptive_delay_mutex_sample_rate, 10,
|
||||
"Sample 1 in N mutex/cv operations for delay")
|
||||
TSAN_FLAG(const char*, adaptive_delay_max_atomic, "sleep_us=50",
|
||||
"Delay for atomic operations: 'spin=N' (max N spins), 'yield', or "
|
||||
"'sleep_us=N' (max N>0 us sleep)")
|
||||
TSAN_FLAG(const char*, adaptive_delay_max_sync, "sleep_us=500",
|
||||
"Delay for sync operations: 'spin=N' (max N spins), 'yield', or "
|
||||
"'sleep_us=N' (max N>0 us sleep)")
|
||||
+24
-2
@@ -34,6 +34,7 @@
|
||||
#if SANITIZER_APPLE && !SANITIZER_GO
|
||||
# include "tsan_flags.h"
|
||||
#endif
|
||||
#include "tsan_adaptive_delay.h"
|
||||
#include "tsan_interceptors.h"
|
||||
#include "tsan_interface.h"
|
||||
#include "tsan_mman.h"
|
||||
@@ -1065,6 +1066,9 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
|
||||
ThreadStart(thr, p->tid, GetTid(), ThreadType::Regular);
|
||||
p->started.Post();
|
||||
}
|
||||
|
||||
AdaptiveDelay::BeforeChildThreadRuns();
|
||||
|
||||
void *res = callback(param);
|
||||
// Prevent the callback from being tail called,
|
||||
// it mixes up stack traces.
|
||||
@@ -1128,6 +1132,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
|
||||
}
|
||||
if (attr == &myattr)
|
||||
pthread_attr_destroy(&myattr);
|
||||
AdaptiveDelay::AfterThreadCreation();
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1423,6 +1428,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
|
||||
TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m);
|
||||
MutexPreLock(thr, pc, (uptr)m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = BLOCK_REAL(pthread_mutex_lock)(m);
|
||||
if (res == errno_EOWNERDEAD)
|
||||
MutexRepair(thr, pc, (uptr)m);
|
||||
@@ -1435,6 +1441,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_mutex_trylock)(m);
|
||||
if (res == errno_EOWNERDEAD)
|
||||
MutexRepair(thr, pc, (uptr)m);
|
||||
@@ -1446,6 +1453,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
|
||||
#if !SANITIZER_APPLE
|
||||
TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_mutex_timedlock)(m, abstime);
|
||||
if (res == 0) {
|
||||
MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
|
||||
@@ -1458,6 +1466,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m);
|
||||
MutexUnlock(thr, pc, (uptr)m);
|
||||
int res = REAL(pthread_mutex_unlock)(m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
if (res == errno_EINVAL)
|
||||
MutexInvalidAccess(thr, pc, (uptr)m);
|
||||
return res;
|
||||
@@ -1468,6 +1477,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_clocklock, void *m,
|
||||
__sanitizer_clockid_t clock, void *abstime) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_clocklock, m, clock, abstime);
|
||||
MutexPreLock(thr, pc, (uptr)m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = BLOCK_REAL(pthread_mutex_clocklock)(m, clock, abstime);
|
||||
if (res == errno_EOWNERDEAD)
|
||||
MutexRepair(thr, pc, (uptr)m);
|
||||
@@ -1486,6 +1496,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_clocklock, void *m,
|
||||
TSAN_INTERCEPTOR(int, __pthread_mutex_lock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(__pthread_mutex_lock, m);
|
||||
MutexPreLock(thr, pc, (uptr)m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = BLOCK_REAL(__pthread_mutex_lock)(m);
|
||||
if (res == errno_EOWNERDEAD)
|
||||
MutexRepair(thr, pc, (uptr)m);
|
||||
@@ -1500,6 +1511,7 @@ TSAN_INTERCEPTOR(int, __pthread_mutex_unlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(__pthread_mutex_unlock, m);
|
||||
MutexUnlock(thr, pc, (uptr)m);
|
||||
int res = REAL(__pthread_mutex_unlock)(m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
if (res == errno_EINVAL)
|
||||
MutexInvalidAccess(thr, pc, (uptr)m);
|
||||
return res;
|
||||
@@ -1529,6 +1541,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
|
||||
TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
|
||||
MutexPreLock(thr, pc, (uptr)m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = BLOCK_REAL(pthread_spin_lock)(m);
|
||||
if (res == 0) {
|
||||
MutexPostLock(thr, pc, (uptr)m);
|
||||
@@ -1538,6 +1551,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_spin_trylock)(m);
|
||||
if (res == 0) {
|
||||
MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
|
||||
@@ -1549,6 +1563,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m);
|
||||
MutexUnlock(thr, pc, (uptr)m);
|
||||
int res = REAL(pthread_spin_unlock)(m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
@@ -1574,6 +1589,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) {
|
||||
TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m);
|
||||
MutexPreReadLock(thr, pc, (uptr)m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_rwlock_rdlock)(m);
|
||||
if (res == 0) {
|
||||
MutexPostReadLock(thr, pc, (uptr)m);
|
||||
@@ -1583,6 +1599,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_rwlock_tryrdlock)(m);
|
||||
if (res == 0) {
|
||||
MutexPostReadLock(thr, pc, (uptr)m, MutexFlagTryLock);
|
||||
@@ -1593,6 +1610,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
|
||||
#if !SANITIZER_APPLE
|
||||
TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
|
||||
if (res == 0) {
|
||||
MutexPostReadLock(thr, pc, (uptr)m);
|
||||
@@ -1604,6 +1622,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
|
||||
TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
|
||||
MutexPreLock(thr, pc, (uptr)m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = BLOCK_REAL(pthread_rwlock_wrlock)(m);
|
||||
if (res == 0) {
|
||||
MutexPostLock(thr, pc, (uptr)m);
|
||||
@@ -1613,6 +1632,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_rwlock_trywrlock)(m);
|
||||
if (res == 0) {
|
||||
MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
|
||||
@@ -1623,6 +1643,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
|
||||
#if !SANITIZER_APPLE
|
||||
TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
|
||||
AdaptiveDelay::SyncOp();
|
||||
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
|
||||
if (res == 0) {
|
||||
MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
|
||||
@@ -1635,6 +1656,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
|
||||
MutexReadOrWriteUnlock(thr, pc, (uptr)m);
|
||||
int res = REAL(pthread_rwlock_unlock)(m);
|
||||
AdaptiveDelay::SyncOp();
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2574,9 +2596,9 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
|
||||
|
||||
#define COMMON_INTERCEPTOR_DLOPEN(filename, flag) \
|
||||
({ \
|
||||
CheckNoDeepBind(filename, flag); \
|
||||
OnDlOpen(filename, flag); \
|
||||
ThreadIgnoreBegin(thr, 0); \
|
||||
void *res = REAL(dlopen)(filename, flag); \
|
||||
void* res = REAL(dlopen)(filename, flag); \
|
||||
ThreadIgnoreEnd(thr); \
|
||||
res; \
|
||||
})
|
||||
|
||||
Vendored
+9
-5
@@ -9,17 +9,19 @@
|
||||
// This file is a part of ThreadSanitizer (TSan), a race detector.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "tsan_interface_ann.h"
|
||||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "sanitizer_common/sanitizer_vector.h"
|
||||
#include "tsan_interface_ann.h"
|
||||
#include "tsan_adaptive_delay.h"
|
||||
#include "tsan_flags.h"
|
||||
#include "tsan_mman.h"
|
||||
#include "tsan_platform.h"
|
||||
#include "tsan_report.h"
|
||||
#include "tsan_rtl.h"
|
||||
#include "tsan_mman.h"
|
||||
#include "tsan_flags.h"
|
||||
#include "tsan_platform.h"
|
||||
|
||||
#define CALLERPC ((uptr)__builtin_return_address(0))
|
||||
|
||||
@@ -370,6 +372,7 @@ void __tsan_mutex_pre_lock(void *m, unsigned flagz) {
|
||||
}
|
||||
ThreadIgnoreBegin(thr, 0);
|
||||
ThreadIgnoreSyncBegin(thr, 0);
|
||||
AdaptiveDelay::SyncOp();
|
||||
}
|
||||
|
||||
INTERFACE_ATTRIBUTE
|
||||
@@ -402,6 +405,7 @@ int __tsan_mutex_pre_unlock(void *m, unsigned flagz) {
|
||||
|
||||
INTERFACE_ATTRIBUTE
|
||||
void __tsan_mutex_post_unlock(void *m, unsigned flagz) {
|
||||
AdaptiveDelay::SyncOp();
|
||||
SCOPED_ANNOTATION(__tsan_mutex_post_unlock);
|
||||
ThreadIgnoreSyncEnd(thr);
|
||||
ThreadIgnoreEnd(thr);
|
||||
|
||||
+12
@@ -21,6 +21,7 @@
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "tsan_adaptive_delay.h"
|
||||
#include "tsan_flags.h"
|
||||
#include "tsan_interface.h"
|
||||
#include "tsan_rtl.h"
|
||||
@@ -520,8 +521,19 @@ static morder to_morder(int mo) {
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
ALWAYS_INLINE auto AtomicDelayImpl(morder mo, Types... args) {
|
||||
AdaptiveDelay::AtomicOpFence(mo);
|
||||
}
|
||||
|
||||
template <class AddrType, class... Types>
|
||||
ALWAYS_INLINE auto AtomicDelayImpl(morder mo, AddrType addr, Types... args) {
|
||||
AdaptiveDelay::AtomicOpAddr((uptr)addr, (int)mo);
|
||||
}
|
||||
|
||||
template <class Op, class... Types>
|
||||
ALWAYS_INLINE auto AtomicImpl(morder mo, Types... args) {
|
||||
AtomicDelayImpl(mo, args...);
|
||||
ThreadState *const thr = cur_thread();
|
||||
ProcessPendingSignals(thr);
|
||||
if (UNLIKELY(thr->ignore_sync || thr->ignore_interceptors))
|
||||
|
||||
Vendored
+10
-7
@@ -404,7 +404,7 @@ struct MappingRiscv64_39 {
|
||||
static const uptr kHeapMemBeg = 0x2c00000000ull;
|
||||
static const uptr kHeapMemEnd = 0x2c00000000ull;
|
||||
static const uptr kHiAppMemBeg = 0x3c00000000ull;
|
||||
static const uptr kHiAppMemEnd = 0x3fffffffffull;
|
||||
static const uptr kHiAppMemEnd = 0x4000000000ull;
|
||||
static const uptr kShadowMsk = 0x3800000000ull;
|
||||
static const uptr kShadowXor = 0x0800000000ull;
|
||||
static const uptr kShadowAdd = 0x0000000000ull;
|
||||
@@ -434,7 +434,7 @@ struct MappingRiscv64_48 {
|
||||
static const uptr kHeapMemBeg = 0x5a0000000000ull;
|
||||
static const uptr kHeapMemEnd = 0x5a0000000000ull;
|
||||
static const uptr kHiAppMemBeg = 0x7a0000000000ull;
|
||||
static const uptr kHiAppMemEnd = 0x7fffffffffffull;
|
||||
static const uptr kHiAppMemEnd = 0x800000000000ull;
|
||||
static const uptr kShadowMsk = 0x700000000000ull;
|
||||
static const uptr kShadowXor = 0x100000000000ull;
|
||||
static const uptr kShadowAdd = 0x000000000000ull;
|
||||
@@ -738,13 +738,16 @@ struct MappingGoRiscv64_48 {
|
||||
Go on linux/s390x
|
||||
0000 0000 1000 - 1000 0000 0000: executable and heap - 16 TiB
|
||||
1000 0000 0000 - 4000 0000 0000: -
|
||||
4000 0000 0000 - 6000 0000 0000: shadow - 64TiB (4 * app)
|
||||
6000 0000 0000 - 9000 0000 0000: -
|
||||
9000 0000 0000 - 9800 0000 0000: metainfo - 8TiB (0.5 * app)
|
||||
4000 0000 0000 - 6000 0000 0000: shadow - 32 TiB (2 * app)
|
||||
6000 0000 0000 - 7000 0000 0000: -
|
||||
7000 0000 0000 - 7800 0000 0000: metainfo - 8 TiB (0.5 * app)
|
||||
7800 0000 0000 - 8000 0000 0000: -
|
||||
*/
|
||||
struct MappingGoS390x {
|
||||
static const uptr kMetaShadowBeg = 0x900000000000ull;
|
||||
static const uptr kMetaShadowEnd = 0x980000000000ull;
|
||||
// Keep the mapping below 2^47 for QEMU linux-user on x86-64 hosts with
|
||||
// four-level page tables.
|
||||
static const uptr kMetaShadowBeg = 0x700000000000ull;
|
||||
static const uptr kMetaShadowEnd = 0x780000000000ull;
|
||||
static const uptr kShadowBeg = 0x400000000000ull;
|
||||
static const uptr kShadowEnd = 0x600000000000ull;
|
||||
static const uptr kLoAppMemBeg = 0x000000001000ull;
|
||||
|
||||
Vendored
+12
@@ -27,6 +27,18 @@
|
||||
#include "tsan_platform.h"
|
||||
#include "tsan_rtl.h"
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
# // for __lwp_gettcb_fast() / __lwp_getprivate_fast()
|
||||
# define _RTLD_SOURCE
|
||||
# include <sys/types.h>
|
||||
# include <machine/mcontext.h>
|
||||
# undef _RTLD_SOURCE
|
||||
# include <sys/param.h>
|
||||
# if __NetBSD_Version__ >= 1099001200
|
||||
# include <machine/lwp_private.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
Vendored
+3
-2
@@ -317,8 +317,9 @@ void PrintReport(const ReportDesc *rep) {
|
||||
} else {
|
||||
PrintStack(rep->stacks[i]);
|
||||
if (i == 0)
|
||||
Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
|
||||
"to get more informative warning message\n\n");
|
||||
Printf(
|
||||
" HINT: use TSAN_OPTIONS=second_deadlock_stack=1 "
|
||||
"to get more informative warning message\n\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Vendored
+5
@@ -21,6 +21,7 @@
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
#include "tsan_adaptive_delay.h"
|
||||
#include "tsan_defs.h"
|
||||
#include "tsan_interface.h"
|
||||
#include "tsan_mman.h"
|
||||
@@ -775,6 +776,10 @@ void Initialize(ThreadState *thr) {
|
||||
while (__tsan_resumed == 0) {}
|
||||
}
|
||||
|
||||
#if !SANITIZER_GO
|
||||
AdaptiveDelay::Init();
|
||||
#endif
|
||||
|
||||
OnInitialize();
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
@@ -34,6 +34,7 @@
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "sanitizer_common/sanitizer_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_vector.h"
|
||||
#include "tsan_adaptive_delay.h"
|
||||
#include "tsan_defs.h"
|
||||
#include "tsan_flags.h"
|
||||
#include "tsan_ignoreset.h"
|
||||
@@ -240,6 +241,8 @@ struct alignas(SANITIZER_CACHE_LINE_SIZE) ThreadState {
|
||||
bool in_internal_write_call;
|
||||
#endif
|
||||
|
||||
AdaptiveDelayState adaptive_delay_state;
|
||||
|
||||
explicit ThreadState(Tid tid);
|
||||
};
|
||||
|
||||
|
||||
Vendored
+4
@@ -41,6 +41,10 @@ extern "C" {
|
||||
// override the default flag values.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
const char *__ubsan_default_options();
|
||||
// Users may provide their own implementation of __ubsan_default_suppressions to
|
||||
// override the default suppression values.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *
|
||||
__ubsan_default_suppressions();
|
||||
} // extern "C"
|
||||
|
||||
#endif // UBSAN_FLAGS_H
|
||||
@@ -350,6 +350,7 @@ fn addCcArgs(target: *const std.Target, args: *std.array_list.Managed([]const u8
|
||||
}
|
||||
|
||||
const tsan_sources = [_][]const u8{
|
||||
"tsan_adaptive_delay.cpp",
|
||||
"tsan_debugging.cpp",
|
||||
"tsan_external.cpp",
|
||||
"tsan_fd.cpp",
|
||||
|
||||
Reference in new issue
Block a user