libtsan: Update to LLVM 20.
This commit is contained in:
12
lib/tsan/builtins/assembly.h
vendored
12
lib/tsan/builtins/assembly.h
vendored
@@ -290,4 +290,16 @@
|
||||
CFI_END
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
#include "int_endianness.h"
|
||||
|
||||
#if _YUGA_BIG_ENDIAN
|
||||
#define VMOV_TO_DOUBLE(dst, src0, src1) vmov dst, src1, src0 SEPARATOR
|
||||
#define VMOV_FROM_DOUBLE(dst0, dst1, src) vmov dst1, dst0, src SEPARATOR
|
||||
#else
|
||||
#define VMOV_TO_DOUBLE(dst, src0, src1) vmov dst, src0, src1 SEPARATOR
|
||||
#define VMOV_FROM_DOUBLE(dst0, dst1, src) vmov dst0, dst1, src SEPARATOR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // COMPILERRT_ASSEMBLY_H
|
||||
|
||||
25
lib/tsan/interception/interception.h
vendored
25
lib/tsan/interception/interception.h
vendored
@@ -25,8 +25,19 @@
|
||||
|
||||
// These typedefs should be used only in the interceptor definitions to replace
|
||||
// the standard system types (e.g. SSIZE_T instead of ssize_t)
|
||||
typedef __sanitizer::uptr SIZE_T;
|
||||
typedef __sanitizer::sptr SSIZE_T;
|
||||
// On Windows the system headers (basetsd.h) provide a conflicting definition
|
||||
// of SIZE_T/SSIZE_T that do not match the real size_t/ssize_t for 32-bit
|
||||
// systems (using long instead of the expected int). Work around the typedef
|
||||
// redefinition by #defining SIZE_T instead of using a typedef.
|
||||
// TODO: We should be using __sanitizer::usize (and a new ssize) instead of
|
||||
// these new macros as long as we ensure they match the real system definitions.
|
||||
#if SANITIZER_WINDOWS
|
||||
// Ensure that (S)SIZE_T were already defined as we are about to override them.
|
||||
# include <basetsd.h>
|
||||
#endif
|
||||
|
||||
#define SIZE_T __sanitizer::usize
|
||||
#define SSIZE_T __sanitizer::ssize
|
||||
typedef __sanitizer::sptr PTRDIFF_T;
|
||||
typedef __sanitizer::s64 INTMAX_T;
|
||||
typedef __sanitizer::u64 UINTMAX_T;
|
||||
@@ -338,16 +349,8 @@ const interpose_substitution substitution_##func_name[] \
|
||||
#endif
|
||||
|
||||
// ISO C++ forbids casting between pointer-to-function and pointer-to-object,
|
||||
// so we use casting via an integral type __interception::uptr,
|
||||
// assuming that system is POSIX-compliant. Using other hacks seem
|
||||
// challenging, as we don't even pass function type to
|
||||
// INTERCEPT_FUNCTION macro, only its name.
|
||||
// so we use casts via uintptr_t (the local __sanitizer::uptr equivalent).
|
||||
namespace __interception {
|
||||
#if defined(_WIN64)
|
||||
typedef unsigned long long uptr;
|
||||
#else
|
||||
typedef unsigned long uptr;
|
||||
#endif // _WIN64
|
||||
|
||||
#if defined(__ELF__) && !SANITIZER_FUCHSIA
|
||||
// The use of interceptors makes many sanitizers unusable for static linking.
|
||||
|
||||
31
lib/tsan/interception/interception_type_test.cpp
vendored
31
lib/tsan/interception/interception_type_test.cpp
vendored
@@ -12,28 +12,35 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "interception.h"
|
||||
#include "sanitizer_common/sanitizer_type_traits.h"
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_APPLE
|
||||
|
||||
#include <sys/types.h>
|
||||
#if __has_include(<sys/types.h>)
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t));
|
||||
COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t));
|
||||
COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t));
|
||||
COMPILER_CHECK((__sanitizer::is_same<__sanitizer::uptr, ::uintptr_t>::value));
|
||||
COMPILER_CHECK((__sanitizer::is_same<__sanitizer::sptr, ::intptr_t>::value));
|
||||
COMPILER_CHECK((__sanitizer::is_same<__sanitizer::usize, ::size_t>::value));
|
||||
COMPILER_CHECK((__sanitizer::is_same<::PTRDIFF_T, ::ptrdiff_t>::value));
|
||||
COMPILER_CHECK((__sanitizer::is_same<::SIZE_T, ::size_t>::value));
|
||||
#if !SANITIZER_WINDOWS
|
||||
// No ssize_t on Windows.
|
||||
COMPILER_CHECK((__sanitizer::is_same<::SSIZE_T, ::ssize_t>::value));
|
||||
#endif
|
||||
// TODO: These are not actually the same type on Linux (long vs long long)
|
||||
COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t));
|
||||
COMPILER_CHECK(sizeof(::UINTMAX_T) == sizeof(uintmax_t));
|
||||
|
||||
# if SANITIZER_GLIBC || SANITIZER_ANDROID
|
||||
#if SANITIZER_GLIBC || SANITIZER_ANDROID
|
||||
COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t));
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// The following are the cases when pread (and friends) is used instead of
|
||||
// pread64. In those cases we need OFF_T to match off_t. We don't care about the
|
||||
// rest (they depend on _FILE_OFFSET_BITS setting when building an application).
|
||||
# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \
|
||||
_FILE_OFFSET_BITS != 64
|
||||
#if !SANITIZER_WINDOWS && (SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \
|
||||
_FILE_OFFSET_BITS != 64)
|
||||
COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t));
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
438
lib/tsan/interception/interception_win.cpp
vendored
438
lib/tsan/interception/interception_win.cpp
vendored
@@ -27,7 +27,7 @@
|
||||
//
|
||||
// 1) Detour
|
||||
//
|
||||
// The Detour hooking technique is assuming the presence of an header with
|
||||
// The Detour hooking technique is assuming the presence of a header with
|
||||
// padding and an overridable 2-bytes nop instruction (mov edi, edi). The
|
||||
// nop instruction can safely be replaced by a 2-bytes jump without any need
|
||||
// to save the instruction. A jump to the target is encoded in the function
|
||||
@@ -47,7 +47,7 @@
|
||||
//
|
||||
// func: jmp <label> --> func: jmp <hook>
|
||||
//
|
||||
// On an 64-bit architecture, a trampoline is inserted.
|
||||
// On a 64-bit architecture, a trampoline is inserted.
|
||||
//
|
||||
// func: jmp <label> --> func: jmp <tramp>
|
||||
// [...]
|
||||
@@ -60,7 +60,7 @@
|
||||
//
|
||||
// 3) HotPatch
|
||||
//
|
||||
// The HotPatch hooking is assuming the presence of an header with padding
|
||||
// The HotPatch hooking is assuming the presence of a header with padding
|
||||
// and a first instruction with at least 2-bytes.
|
||||
//
|
||||
// The reason to enforce the 2-bytes limitation is to provide the minimal
|
||||
@@ -80,7 +80,7 @@
|
||||
// real: <instr>
|
||||
// jmp <body>
|
||||
//
|
||||
// On an 64-bit architecture:
|
||||
// On a 64-bit architecture:
|
||||
//
|
||||
// head: 6 x nop head: jmp QWORD [addr1]
|
||||
// func: <instr> --> func: jmp short <head>
|
||||
@@ -110,7 +110,7 @@
|
||||
// <instr>
|
||||
// jmp <body>
|
||||
//
|
||||
// On an 64-bit architecture:
|
||||
// On a 64-bit architecture:
|
||||
//
|
||||
// func: <instr> --> func: jmp QWORD [addr1]
|
||||
// <instr>
|
||||
@@ -130,6 +130,7 @@
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
namespace __interception {
|
||||
|
||||
@@ -186,8 +187,12 @@ static uptr GetMmapGranularity() {
|
||||
return si.dwAllocationGranularity;
|
||||
}
|
||||
|
||||
UNUSED static uptr RoundDownTo(uptr size, uptr boundary) {
|
||||
return size & ~(boundary - 1);
|
||||
}
|
||||
|
||||
UNUSED static uptr RoundUpTo(uptr size, uptr boundary) {
|
||||
return (size + boundary - 1) & ~(boundary - 1);
|
||||
return RoundDownTo(size + boundary - 1, boundary);
|
||||
}
|
||||
|
||||
// FIXME: internal_str* and internal_mem* functions should be moved from the
|
||||
@@ -208,6 +213,18 @@ static char* _strchr(char* str, char c) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int _strcmp(const char *s1, const char *s2) {
|
||||
while (true) {
|
||||
unsigned c1 = *s1;
|
||||
unsigned c2 = *s2;
|
||||
if (c1 != c2) return (c1 < c2) ? -1 : 1;
|
||||
if (c1 == 0) break;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _memset(void *p, int value, size_t sz) {
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
((char*)p)[i] = (char)value;
|
||||
@@ -284,8 +301,11 @@ static void WriteJumpInstruction(uptr from, uptr target) {
|
||||
|
||||
static void WriteShortJumpInstruction(uptr from, uptr target) {
|
||||
sptr offset = target - from - kShortJumpInstructionLength;
|
||||
if (offset < -128 || offset > 127)
|
||||
if (offset < -128 || offset > 127) {
|
||||
ReportError("interception_win: cannot write short jmp from %p to %p\n",
|
||||
(void *)from, (void *)target);
|
||||
InterceptionFailed();
|
||||
}
|
||||
*(u8*)from = 0xEB;
|
||||
*(u8*)(from + 1) = (u8)offset;
|
||||
}
|
||||
@@ -339,32 +359,78 @@ struct TrampolineMemoryRegion {
|
||||
uptr max_size;
|
||||
};
|
||||
|
||||
UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig
|
||||
UNUSED static const uptr kTrampolineRangeLimit = 1ull << 31; // 2 gig
|
||||
static const int kMaxTrampolineRegion = 1024;
|
||||
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
|
||||
|
||||
static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
|
||||
#if SANITIZER_WINDOWS64
|
||||
uptr address = image_address;
|
||||
uptr scanned = 0;
|
||||
while (scanned < kTrampolineScanLimitRange) {
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
if (!::VirtualQuery((void*)address, &info, sizeof(info)))
|
||||
return nullptr;
|
||||
static void *AllocateTrampolineRegion(uptr min_addr, uptr max_addr,
|
||||
uptr func_addr, size_t granularity) {
|
||||
# if SANITIZER_WINDOWS64
|
||||
// Clamp {min,max}_addr to the accessible address space.
|
||||
SYSTEM_INFO system_info;
|
||||
::GetSystemInfo(&system_info);
|
||||
uptr min_virtual_addr =
|
||||
RoundUpTo((uptr)system_info.lpMinimumApplicationAddress, granularity);
|
||||
uptr max_virtual_addr =
|
||||
RoundDownTo((uptr)system_info.lpMaximumApplicationAddress, granularity);
|
||||
if (min_addr < min_virtual_addr)
|
||||
min_addr = min_virtual_addr;
|
||||
if (max_addr > max_virtual_addr)
|
||||
max_addr = max_virtual_addr;
|
||||
|
||||
// Check whether a region can be allocated at |address|.
|
||||
// This loop probes the virtual address space to find free memory in the
|
||||
// [min_addr, max_addr] interval. The search starts from func_addr and
|
||||
// proceeds "outwards" towards the interval bounds using two probes, lo_addr
|
||||
// and hi_addr, for addresses lower/higher than func_addr. At each step, it
|
||||
// considers the probe closest to func_addr. If that address is not free, the
|
||||
// probe is advanced (lower or higher depending on the probe) to the next
|
||||
// memory block and the search continues.
|
||||
uptr lo_addr = RoundDownTo(func_addr, granularity);
|
||||
uptr hi_addr = RoundUpTo(func_addr, granularity);
|
||||
while (lo_addr >= min_addr || hi_addr <= max_addr) {
|
||||
// Consider the in-range address closest to func_addr.
|
||||
uptr addr;
|
||||
if (lo_addr < min_addr)
|
||||
addr = hi_addr;
|
||||
else if (hi_addr > max_addr)
|
||||
addr = lo_addr;
|
||||
else
|
||||
addr = (hi_addr - func_addr < func_addr - lo_addr) ? hi_addr : lo_addr;
|
||||
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
if (!::VirtualQuery((void *)addr, &info, sizeof(info))) {
|
||||
ReportError(
|
||||
"interception_win: VirtualQuery in AllocateTrampolineRegion failed "
|
||||
"for %p\n",
|
||||
(void *)addr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check whether a region can be allocated at |addr|.
|
||||
if (info.State == MEM_FREE && info.RegionSize >= granularity) {
|
||||
void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
|
||||
granularity,
|
||||
MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
void *page =
|
||||
::VirtualAlloc((void *)addr, granularity, MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
if (page == nullptr)
|
||||
ReportError(
|
||||
"interception_win: VirtualAlloc in AllocateTrampolineRegion failed "
|
||||
"for %p\n",
|
||||
(void *)addr);
|
||||
return page;
|
||||
}
|
||||
|
||||
// Move to the next region.
|
||||
address = (uptr)info.BaseAddress + info.RegionSize;
|
||||
scanned += info.RegionSize;
|
||||
if (addr == lo_addr)
|
||||
lo_addr =
|
||||
RoundDownTo((uptr)info.AllocationBase - granularity, granularity);
|
||||
if (addr == hi_addr)
|
||||
hi_addr =
|
||||
RoundUpTo((uptr)info.BaseAddress + info.RegionSize, granularity);
|
||||
}
|
||||
|
||||
ReportError(
|
||||
"interception_win: AllocateTrampolineRegion failed to find free memory; "
|
||||
"min_addr: %p, max_addr: %p, func_addr: %p, granularity: %zu\n",
|
||||
(void *)min_addr, (void *)max_addr, (void *)func_addr, granularity);
|
||||
return nullptr;
|
||||
#else
|
||||
return ::VirtualAlloc(nullptr,
|
||||
@@ -385,15 +451,51 @@ void TestOnlyReleaseTrampolineRegions() {
|
||||
}
|
||||
}
|
||||
|
||||
static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
|
||||
// Find a region within 2G with enough space to allocate |size| bytes.
|
||||
static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
|
||||
# if SANITIZER_WINDOWS64
|
||||
uptr min_addr = func_address - kTrampolineRangeLimit;
|
||||
uptr max_addr = func_address + kTrampolineRangeLimit - size;
|
||||
|
||||
// Allocate memory within 2GB of the module (DLL or EXE file) so that any
|
||||
// address within the module can be referenced with PC-relative operands.
|
||||
// This allows us to not just jump to the trampoline with a PC-relative
|
||||
// offset, but to relocate any instructions that we copy to the trampoline
|
||||
// which have references to the original module. If we can't find the base
|
||||
// address of the module (e.g. if func_address is in mmap'ed memory), just
|
||||
// stay within 2GB of func_address.
|
||||
HMODULE module;
|
||||
if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCWSTR)func_address, &module)) {
|
||||
MODULEINFO module_info;
|
||||
if (::GetModuleInformation(::GetCurrentProcess(), module,
|
||||
&module_info, sizeof(module_info))) {
|
||||
min_addr = (uptr)module_info.lpBaseOfDll + module_info.SizeOfImage -
|
||||
kTrampolineRangeLimit;
|
||||
max_addr = (uptr)module_info.lpBaseOfDll + kTrampolineRangeLimit - size;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for overflow.
|
||||
if (min_addr > func_address)
|
||||
min_addr = 0;
|
||||
if (max_addr < func_address)
|
||||
max_addr = ~(uptr)0;
|
||||
# else
|
||||
uptr min_addr = 0;
|
||||
uptr max_addr = ~min_addr;
|
||||
# endif
|
||||
|
||||
// Find a region within [min_addr,max_addr] with enough space to allocate
|
||||
// |size| bytes.
|
||||
TrampolineMemoryRegion *region = nullptr;
|
||||
for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
|
||||
TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
|
||||
if (current->content == 0) {
|
||||
// No valid region found, allocate a new region.
|
||||
size_t bucket_size = GetMmapGranularity();
|
||||
void *content = AllocateTrampolineRegion(image_address, bucket_size);
|
||||
void *content = AllocateTrampolineRegion(min_addr, max_addr, func_address,
|
||||
bucket_size);
|
||||
if (content == nullptr)
|
||||
return 0U;
|
||||
|
||||
@@ -403,13 +505,9 @@ static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
|
||||
region = current;
|
||||
break;
|
||||
} else if (current->max_size - current->allocated_size > size) {
|
||||
#if SANITIZER_WINDOWS64
|
||||
// In 64-bits, the memory space must be allocated within 2G boundary.
|
||||
uptr next_address = current->content + current->allocated_size;
|
||||
if (next_address < image_address ||
|
||||
next_address - image_address >= 0x7FFF0000)
|
||||
continue;
|
||||
#endif
|
||||
uptr next_address = current->content + current->allocated_size;
|
||||
if (next_address < min_addr || next_address > max_addr)
|
||||
continue;
|
||||
// The space can be allocated in the current region.
|
||||
region = current;
|
||||
break;
|
||||
@@ -458,6 +556,10 @@ static const u8 kPrologueWithShortJump2[] = {
|
||||
|
||||
// Returns 0 on error.
|
||||
static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
if (rel_offset) {
|
||||
*rel_offset = 0;
|
||||
}
|
||||
|
||||
#if SANITIZER_ARM64
|
||||
// An ARM64 instruction is 4 bytes long.
|
||||
return 4;
|
||||
@@ -497,8 +599,14 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0x6A: // 6A XX = push XX
|
||||
return 2;
|
||||
|
||||
// This instruction can be encoded with a 16-bit immediate but that is
|
||||
// incredibly unlikely.
|
||||
case 0x68: // 68 XX XX XX XX : push imm32
|
||||
return 5;
|
||||
|
||||
case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX
|
||||
case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX
|
||||
case 0xBA: // ba XX XX XX XX : mov edx, XX XX XX XX
|
||||
return 5;
|
||||
|
||||
// Cannot overwrite control-instruction. Return 0 to indicate failure.
|
||||
@@ -529,19 +637,43 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0xFF8B: // 8B FF : mov edi, edi
|
||||
case 0xEC8B: // 8B EC : mov ebp, esp
|
||||
case 0xc889: // 89 C8 : mov eax, ecx
|
||||
case 0xD189: // 89 D1 : mov ecx, edx
|
||||
case 0xE589: // 89 E5 : mov ebp, esp
|
||||
case 0xC18B: // 8B C1 : mov eax, ecx
|
||||
case 0xC031: // 31 C0 : xor eax, eax
|
||||
case 0xC931: // 31 C9 : xor ecx, ecx
|
||||
case 0xD231: // 31 D2 : xor edx, edx
|
||||
case 0xC033: // 33 C0 : xor eax, eax
|
||||
case 0xC933: // 33 C9 : xor ecx, ecx
|
||||
case 0xD233: // 33 D2 : xor edx, edx
|
||||
case 0xDB84: // 84 DB : test bl,bl
|
||||
case 0xC084: // 84 C0 : test al,al
|
||||
case 0xC984: // 84 C9 : test cl,cl
|
||||
case 0xD284: // 84 D2 : test dl,dl
|
||||
return 2;
|
||||
|
||||
case 0x3980: // 80 39 XX : cmp BYTE PTR [rcx], 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
|
||||
case 0xE483: // 83 E4 XX : and esp, XX
|
||||
case 0xEC83: // 83 EC XX : sub esp, XX
|
||||
case 0xC1F6: // F6 C1 XX : test cl, XX
|
||||
return 3;
|
||||
|
||||
case 0x89FF: // FF 89 XX XX XX XX : dec dword ptr [ecx + XX XX XX XX]
|
||||
case 0xEC81: // 81 EC XX XX XX XX : sub esp, XX XX XX XX
|
||||
return 6;
|
||||
|
||||
// Cannot overwrite control-instruction. Return 0 to indicate failure.
|
||||
case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX]
|
||||
case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (0x00FFFFFF & *(u32*)address) {
|
||||
switch (0x00FFFFFF & *(u32 *)address) {
|
||||
case 0x244C8D: // 8D 4C 24 XX : lea ecx, [esp + XX]
|
||||
case 0x2474FF: // FF 74 24 XX : push qword ptr [rsp + XX]
|
||||
return 4;
|
||||
case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]
|
||||
return 7;
|
||||
}
|
||||
@@ -556,6 +688,21 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0xA1: // A1 XX XX XX XX XX XX XX XX :
|
||||
// movabs eax, dword ptr ds:[XXXXXXXX]
|
||||
return 9;
|
||||
case 0xF2:
|
||||
switch (*(u32 *)(address + 1)) {
|
||||
case 0x2444110f: // f2 0f 11 44 24 XX movsd QWORD PTR
|
||||
// [rsp + XX], xmm0
|
||||
case 0x244c110f: // f2 0f 11 4c 24 XX movsd QWORD PTR
|
||||
// [rsp + XX], xmm1
|
||||
case 0x2454110f: // f2 0f 11 54 24 XX movsd QWORD PTR
|
||||
// [rsp + XX], xmm2
|
||||
case 0x245c110f: // f2 0f 11 5c 24 XX movsd QWORD PTR
|
||||
// [rsp + XX], xmm3
|
||||
case 0x2464110f: // f2 0f 11 64 24 XX movsd QWORD PTR
|
||||
// [rsp + XX], xmm4
|
||||
return 6;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x83:
|
||||
const u8 next_byte = *(u8*)(address + 1);
|
||||
@@ -584,55 +731,155 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0x018a: // mov al, byte ptr [rcx]
|
||||
return 2;
|
||||
|
||||
case 0x7E80: // 80 7E YY XX cmp BYTE PTR [rsi+YY], XX
|
||||
case 0x7D80: // 80 7D YY XX cmp BYTE PTR [rbp+YY], XX
|
||||
case 0x7A80: // 80 7A YY XX cmp BYTE PTR [rdx+YY], XX
|
||||
case 0x7880: // 80 78 YY XX cmp BYTE PTR [rax+YY], XX
|
||||
case 0x7B80: // 80 7B YY XX cmp BYTE PTR [rbx+YY], XX
|
||||
case 0x7980: // 80 79 YY XX cmp BYTE ptr [rcx+YY], XX
|
||||
return 4;
|
||||
|
||||
case 0x058A: // 8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX]
|
||||
case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
|
||||
if (rel_offset)
|
||||
*rel_offset = 2;
|
||||
case 0xB841: // 41 B8 XX XX XX XX : mov r8d, XX XX XX XX
|
||||
return 6;
|
||||
|
||||
case 0x7E81: // 81 7E YY XX XX XX XX cmp DWORD PTR [rsi+YY], XX XX XX XX
|
||||
case 0x7D81: // 81 7D YY XX XX XX XX cmp DWORD PTR [rbp+YY], XX XX XX XX
|
||||
case 0x7A81: // 81 7A YY XX XX XX XX cmp DWORD PTR [rdx+YY], XX XX XX XX
|
||||
case 0x7881: // 81 78 YY XX XX XX XX cmp DWORD PTR [rax+YY], XX XX XX XX
|
||||
case 0x7B81: // 81 7B YY XX XX XX XX cmp DWORD PTR [rbx+YY], XX XX XX XX
|
||||
case 0x7981: // 81 79 YY XX XX XX XX cmp dword ptr [rcx+YY], XX XX XX XX
|
||||
return 7;
|
||||
}
|
||||
|
||||
switch (0x00FFFFFF & *(u32*)address) {
|
||||
case 0xe58948: // 48 8b c4 : mov rbp, rsp
|
||||
case 0xc18b48: // 48 8b c1 : mov rax, rcx
|
||||
case 0xc48b48: // 48 8b c4 : mov rax, rsp
|
||||
case 0xd9f748: // 48 f7 d9 : neg rcx
|
||||
case 0xd12b48: // 48 2b d1 : sub rdx, rcx
|
||||
case 0x07c1f6: // f6 c1 07 : test cl, 0x7
|
||||
case 0xc98548: // 48 85 C9 : test rcx, rcx
|
||||
case 0xd28548: // 48 85 d2 : test rdx, rdx
|
||||
case 0xc0854d: // 4d 85 c0 : test r8, r8
|
||||
case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
|
||||
switch (0x00FFFFFF & *(u32 *)address) {
|
||||
case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
|
||||
case 0xc00b4d: // 4d 0b c0 : or r8, r8
|
||||
case 0xc03345: // 45 33 c0 : xor r8d, r8d
|
||||
case 0xc08548: // 48 85 c0 : test rax, rax
|
||||
case 0xc0854d: // 4d 85 c0 : test r8, r8
|
||||
case 0xc08b41: // 41 8b c0 : mov eax, r8d
|
||||
case 0xc0ff48: // 48 ff c0 : inc rax
|
||||
case 0xc0ff49: // 49 ff c0 : inc r8
|
||||
case 0xc18b41: // 41 8b c1 : mov eax, r9d
|
||||
case 0xc18b48: // 48 8b c1 : mov rax, rcx
|
||||
case 0xc18b4c: // 4c 8b c1 : mov r8, rcx
|
||||
case 0xc1ff48: // 48 ff c1 : inc rcx
|
||||
case 0xc1ff49: // 49 ff c1 : inc r9
|
||||
case 0xc28b41: // 41 8b c2 : mov eax, r10d
|
||||
case 0x01b60f: // 0f b6 01 : movzx eax, BYTE PTR [rcx]
|
||||
case 0x09b60f: // 0f b6 09 : movzx ecx, BYTE PTR [rcx]
|
||||
case 0x11b60f: // 0f b6 11 : movzx edx, BYTE PTR [rcx]
|
||||
case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
|
||||
case 0xc2ff48: // 48 ff c2 : inc rdx
|
||||
case 0xc2ff49: // 49 ff c2 : inc r10
|
||||
case 0xc38b41: // 41 8b c3 : mov eax, r11d
|
||||
case 0xc3ff48: // 48 ff c3 : inc rbx
|
||||
case 0xc3ff49: // 49 ff c3 : inc r11
|
||||
case 0xc48b41: // 41 8b c4 : mov eax, r12d
|
||||
case 0xc48b48: // 48 8b c4 : mov rax, rsp
|
||||
case 0xc4ff49: // 49 ff c4 : inc r12
|
||||
case 0xc5ff49: // 49 ff c5 : inc r13
|
||||
case 0xc6ff48: // 48 ff c6 : inc rsi
|
||||
case 0xc6ff49: // 49 ff c6 : inc r14
|
||||
case 0xc7ff48: // 48 ff c7 : inc rdi
|
||||
case 0xc7ff49: // 49 ff c7 : inc r15
|
||||
case 0xc93345: // 45 33 c9 : xor r9d, r9d
|
||||
case 0xdb3345: // 45 33 DB : xor r11d, r11d
|
||||
case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
|
||||
case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
|
||||
case 0xc98b4c: // 4C 8B C9 : mov r9, rcx
|
||||
case 0xc18b4c: // 4C 8B C1 : mov r8, rcx
|
||||
case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
|
||||
case 0xc98548: // 48 85 c9 : test rcx, rcx
|
||||
case 0xc9854d: // 4d 85 c9 : test r9, r9
|
||||
case 0xc98b4c: // 4c 8b c9 : mov r9, rcx
|
||||
case 0xd12948: // 48 29 d1 : sub rcx, rdx
|
||||
case 0xca2b48: // 48 2b ca : sub rcx, rdx
|
||||
case 0xca3b48: // 48 3b ca : cmp rcx, rdx
|
||||
case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
|
||||
case 0xc00b4d: // 3d 0b c0 : or r8, r8
|
||||
case 0xc08b41: // 41 8b c0 : mov eax, r8d
|
||||
case 0xd12b48: // 48 2b d1 : sub rdx, rcx
|
||||
case 0xd18b48: // 48 8b d1 : mov rdx, rcx
|
||||
case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
|
||||
case 0xd18b4c: // 4c 8b d1 : mov r10, rcx
|
||||
case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0
|
||||
case 0xd28548: // 48 85 d2 : test rdx, rdx
|
||||
case 0xd2854d: // 4d 85 d2 : test r10, r10
|
||||
case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
|
||||
case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
|
||||
case 0xd2be0f: // 0f be d2 : movsx edx, dl
|
||||
case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
|
||||
case 0xd9f748: // 48 f7 d9 : neg rcx
|
||||
case 0xc03145: // 45 31 c0 : xor r8d,r8d
|
||||
case 0xc93145: // 45 31 c9 : xor r9d,r9d
|
||||
case 0xdb3345: // 45 33 db : xor r11d, r11d
|
||||
case 0xc08445: // 45 84 c0 : test r8b,r8b
|
||||
case 0xd28445: // 45 84 d2 : test r10b,r10b
|
||||
case 0xdb8548: // 48 85 db : test rbx, rbx
|
||||
case 0xdb854d: // 4d 85 db : test r11, r11
|
||||
case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
|
||||
case 0xe48548: // 48 85 e4 : test rsp, rsp
|
||||
case 0xe4854d: // 4d 85 e4 : test r12, r12
|
||||
case 0xc88948: // 48 89 c8 : mov rax,rcx
|
||||
case 0xcb8948: // 48 89 cb : mov rbx,rcx
|
||||
case 0xd08948: // 48 89 d0 : mov rax,rdx
|
||||
case 0xd18948: // 48 89 d1 : mov rcx,rdx
|
||||
case 0xd38948: // 48 89 d3 : mov rbx,rdx
|
||||
case 0xe58948: // 48 89 e5 : mov rbp, rsp
|
||||
case 0xed8548: // 48 85 ed : test rbp, rbp
|
||||
case 0xc88949: // 49 89 c8 : mov r8, rcx
|
||||
case 0xc98949: // 49 89 c9 : mov r9, rcx
|
||||
case 0xca8949: // 49 89 ca : mov r10,rcx
|
||||
case 0xd08949: // 49 89 d0 : mov r8, rdx
|
||||
case 0xd18949: // 49 89 d1 : mov r9, rdx
|
||||
case 0xd28949: // 49 89 d2 : mov r10, rdx
|
||||
case 0xd38949: // 49 89 d3 : mov r11, rdx
|
||||
case 0xed854d: // 4d 85 ed : test r13, r13
|
||||
case 0xf6854d: // 4d 85 f6 : test r14, r14
|
||||
case 0xff854d: // 4d 85 ff : test r15, r15
|
||||
return 3;
|
||||
|
||||
case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx
|
||||
case 0x428d44: // 44 8d 42 XX : lea r8d , [rdx + XX]
|
||||
case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
|
||||
case 0xec8348: // 48 83 ec XX : sub rsp, XX
|
||||
case 0xf88349: // 49 83 f8 XX : cmp r8, XX
|
||||
case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
|
||||
case 0x488d49: // 49 8d 48 XX : lea rcx, [...]
|
||||
case 0x048d4c: // 4c 8d 04 XX : lea r8, [...]
|
||||
case 0x148d4e: // 4e 8d 14 XX : lea r10, [...]
|
||||
case 0x398366: // 66 83 39 XX : cmp WORD PTR [rcx], XX
|
||||
return 4;
|
||||
|
||||
case 0x441F0F: // 0F 1F 44 XX XX : nop DWORD PTR [...]
|
||||
case 0x246483: // 83 64 24 XX YY : and DWORD PTR [rsp+XX], YY
|
||||
return 5;
|
||||
|
||||
case 0x788166: // 66 81 78 XX YY YY cmp WORD PTR [rax+XX], YY YY
|
||||
case 0x798166: // 66 81 79 XX YY YY cmp WORD PTR [rcx+XX], YY YY
|
||||
case 0x7a8166: // 66 81 7a XX YY YY cmp WORD PTR [rdx+XX], YY YY
|
||||
case 0x7b8166: // 66 81 7b XX YY YY cmp WORD PTR [rbx+XX], YY YY
|
||||
case 0x7e8166: // 66 81 7e XX YY YY cmp WORD PTR [rsi+XX], YY YY
|
||||
case 0x7f8166: // 66 81 7f XX YY YY cmp WORD PTR [rdi+XX], YY YY
|
||||
return 6;
|
||||
|
||||
case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
|
||||
case 0xc0c748: // 48 C7 C0 XX XX XX XX : mov rax, XX XX XX XX
|
||||
return 7;
|
||||
|
||||
// clang-format off
|
||||
case 0x788141: // 41 81 78 XX YY YY YY YY : cmp DWORD PTR [r8+YY], XX XX XX XX
|
||||
case 0x798141: // 41 81 79 XX YY YY YY YY : cmp DWORD PTR [r9+YY], XX XX XX XX
|
||||
case 0x7a8141: // 41 81 7a XX YY YY YY YY : cmp DWORD PTR [r10+YY], XX XX XX XX
|
||||
case 0x7b8141: // 41 81 7b XX YY YY YY YY : cmp DWORD PTR [r11+YY], XX XX XX XX
|
||||
case 0x7d8141: // 41 81 7d XX YY YY YY YY : cmp DWORD PTR [r13+YY], XX XX XX XX
|
||||
case 0x7e8141: // 41 81 7e XX YY YY YY YY : cmp DWORD PTR [r14+YY], XX XX XX XX
|
||||
case 0x7f8141: // 41 81 7f YY XX XX XX XX : cmp DWORD PTR [r15+YY], XX XX XX XX
|
||||
case 0x247c81: // 81 7c 24 YY XX XX XX XX : cmp DWORD PTR [rsp+YY], XX XX XX XX
|
||||
return 8;
|
||||
// clang-format on
|
||||
|
||||
case 0x058b48: // 48 8b 05 XX XX XX XX :
|
||||
// mov rax, QWORD PTR [rip + XXXXXXXX]
|
||||
case 0x058d48: // 48 8d 05 XX XX XX XX :
|
||||
// lea rax, QWORD PTR [rip + XXXXXXXX]
|
||||
case 0x0d8948: // 48 89 0d XX XX XX XX :
|
||||
// mov QWORD PTR [rip + XXXXXXXX], rcx
|
||||
case 0x158948: // 48 89 15 XX XX XX XX :
|
||||
// mov QWORD PTR [rip + XXXXXXXX], rdx
|
||||
case 0x25ff48: // 48 ff 25 XX XX XX XX :
|
||||
// rex.W jmp QWORD PTR [rip + XXXXXXXX]
|
||||
case 0x158D4C: // 4c 8d 15 XX XX XX XX : lea r10, [rip + XX]
|
||||
@@ -644,9 +891,19 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0x2444c7: // C7 44 24 XX YY YY YY YY
|
||||
// mov dword ptr [rsp + XX], YYYYYYYY
|
||||
return 8;
|
||||
|
||||
case 0x7c8141: // 41 81 7c ZZ YY XX XX XX XX
|
||||
// cmp DWORD PTR [reg+reg*n+YY], XX XX XX XX
|
||||
return 9;
|
||||
}
|
||||
|
||||
switch (*(u32*)(address)) {
|
||||
case 0x01b60f44: // 44 0f b6 01 : movzx r8d, BYTE PTR [rcx]
|
||||
case 0x09b60f44: // 44 0f b6 09 : movzx r9d, BYTE PTR [rcx]
|
||||
case 0x0ab60f44: // 44 0f b6 0a : movzx r8d, BYTE PTR [rdx]
|
||||
case 0x11b60f44: // 44 0f b6 11 : movzx r10d, BYTE PTR [rcx]
|
||||
case 0x1ab60f44: // 44 0f b6 1a : movzx r11d, BYTE PTR [rdx]
|
||||
return 4;
|
||||
case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
|
||||
case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
|
||||
case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
|
||||
@@ -656,9 +913,19 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx
|
||||
case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9
|
||||
case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8
|
||||
case 0x244c8944: // 44 89 4c 24 XX mov DWORD PTR [rsp + XX], r9d
|
||||
case 0x24448944: // 44 89 44 24 XX mov DWORD PTR [rsp + XX], r8d
|
||||
case 0x246c8d48: // 48 8d 6c 24 XX : lea rbp, [rsp + XX]
|
||||
return 5;
|
||||
case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
|
||||
case 0x24648348: // 48 83 64 24 XX YY : and QWORD PTR [rsp + XX], YY
|
||||
return 6;
|
||||
case 0x24A48D48: // 48 8D A4 24 XX XX XX XX : lea rsp, [rsp + XX XX XX XX]
|
||||
return 8;
|
||||
}
|
||||
|
||||
switch (0xFFFFFFFFFFULL & *(u64 *)(address)) {
|
||||
case 0xC07E0F4866: // 66 48 0F 7E C0 : movq rax, xmm0
|
||||
return 5;
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -671,11 +938,10 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX]
|
||||
case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
|
||||
case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX]
|
||||
case 0xEC83: // 83 EC XX : sub esp, XX
|
||||
case 0x758B: // 8B 75 XX : mov esi, dword ptr [ebp + XX]
|
||||
case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX]
|
||||
return 3;
|
||||
case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX
|
||||
case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
|
||||
return 6;
|
||||
case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX
|
||||
return 7;
|
||||
@@ -718,6 +984,10 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t TestOnlyGetInstructionSize(uptr address, size_t *rel_offset) {
|
||||
return GetInstructionSize(address, rel_offset);
|
||||
}
|
||||
|
||||
// Returns 0 on error.
|
||||
static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
|
||||
size_t cursor = 0;
|
||||
@@ -745,8 +1015,14 @@ static bool CopyInstructions(uptr to, uptr from, size_t size) {
|
||||
// this will be untrue if relocated_offset \notin [-2**31, 2**31)
|
||||
s64 delta = to - from;
|
||||
s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
|
||||
if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll)
|
||||
if (-0x8000'0000ll > relocated_offset ||
|
||||
relocated_offset > 0x7FFF'FFFFll) {
|
||||
ReportError(
|
||||
"interception_win: CopyInstructions relocated_offset %lld outside "
|
||||
"32-bit range\n",
|
||||
(long long)relocated_offset);
|
||||
return false;
|
||||
}
|
||||
# else
|
||||
// on 32-bit, the relative offset will always be correct
|
||||
s32 delta = to - from;
|
||||
@@ -969,8 +1245,7 @@ static void **InterestingDLLsAvailable() {
|
||||
"libc++.dll", // libc++
|
||||
"libunwind.dll", // libunwind
|
||||
# endif
|
||||
// NTDLL should go last as it exports some functions that we should
|
||||
// override in the CRT [presumably only used internally].
|
||||
// NTDLL must go last as it gets special treatment in OverrideFunction.
|
||||
"ntdll.dll",
|
||||
NULL
|
||||
};
|
||||
@@ -1027,7 +1302,7 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
|
||||
|
||||
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
|
||||
RVAPtr<char> name(module, names[i]);
|
||||
if (!strcmp(func_name, name)) {
|
||||
if (!_strcmp(func_name, name)) {
|
||||
DWORD index = ordinals[i];
|
||||
RVAPtr<char> func(module, functions[index]);
|
||||
|
||||
@@ -1040,19 +1315,27 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
|
||||
// exported directory.
|
||||
char function_name[256];
|
||||
size_t funtion_name_length = _strlen(func);
|
||||
if (funtion_name_length >= sizeof(function_name) - 1)
|
||||
if (funtion_name_length >= sizeof(function_name) - 1) {
|
||||
ReportError("interception_win: func too long: '%s'\n", (char *)func);
|
||||
InterceptionFailed();
|
||||
}
|
||||
|
||||
_memcpy(function_name, func, funtion_name_length);
|
||||
function_name[funtion_name_length] = '\0';
|
||||
char* separator = _strchr(function_name, '.');
|
||||
if (!separator)
|
||||
if (!separator) {
|
||||
ReportError("interception_win: no separator in '%s'\n",
|
||||
function_name);
|
||||
InterceptionFailed();
|
||||
}
|
||||
*separator = '\0';
|
||||
|
||||
void* redirected_module = GetModuleHandleA(function_name);
|
||||
if (!redirected_module)
|
||||
if (!redirected_module) {
|
||||
ReportError("interception_win: GetModuleHandleA failed for '%s'\n",
|
||||
function_name);
|
||||
InterceptionFailed();
|
||||
}
|
||||
return InternalGetProcAddress(redirected_module, separator + 1);
|
||||
}
|
||||
|
||||
@@ -1065,9 +1348,22 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
|
||||
|
||||
bool OverrideFunction(
|
||||
const char *func_name, uptr new_func, uptr *orig_old_func) {
|
||||
static const char *kNtDllIgnore[] = {
|
||||
"memcmp", "memcpy", "memmove", "memset"
|
||||
};
|
||||
|
||||
bool hooked = false;
|
||||
void **DLLs = InterestingDLLsAvailable();
|
||||
for (size_t i = 0; DLLs[i]; ++i) {
|
||||
if (DLLs[i + 1] == nullptr) {
|
||||
// This is the last DLL, i.e. NTDLL. It exports some functions that
|
||||
// we only want to override in the CRT.
|
||||
for (const char *ignored : kNtDllIgnore) {
|
||||
if (_strcmp(func_name, ignored) == 0)
|
||||
return hooked;
|
||||
}
|
||||
}
|
||||
|
||||
uptr func_addr = InternalGetProcAddress(DLLs[i], func_name);
|
||||
if (func_addr &&
|
||||
OverrideFunction(func_addr, new_func, orig_old_func)) {
|
||||
@@ -1121,7 +1417,7 @@ bool OverrideImportedFunction(const char *module_to_patch,
|
||||
RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name(
|
||||
module, name_table->u1.ForwarderString);
|
||||
const char *funcname = &import_by_name->Name[0];
|
||||
if (strcmp(funcname, function_name) == 0)
|
||||
if (_strcmp(funcname, function_name) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1144,4 +1440,4 @@ bool OverrideImportedFunction(const char *module_to_patch,
|
||||
|
||||
} // namespace __interception
|
||||
|
||||
#endif // SANITIZER_APPLE
|
||||
#endif // SANITIZER_WINDOWS
|
||||
|
||||
3
lib/tsan/interception/interception_win.h
vendored
3
lib/tsan/interception/interception_win.h
vendored
@@ -63,6 +63,9 @@ bool OverrideFunctionWithTrampoline(
|
||||
// Exposed for unittests
|
||||
void TestOnlyReleaseTrampolineRegions();
|
||||
|
||||
// Exposed for unittests
|
||||
SIZE_T TestOnlyGetInstructionSize(uptr address, SIZE_T *rel_offset);
|
||||
|
||||
} // namespace __interception
|
||||
|
||||
#if defined(INTERCEPTION_DYNAMIC_CRT)
|
||||
|
||||
@@ -59,7 +59,7 @@ static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
|
||||
|
||||
static void *RawInternalRealloc(void *ptr, uptr size,
|
||||
InternalAllocatorCache *cache) {
|
||||
uptr alignment = 8;
|
||||
constexpr usize alignment = Max<usize>(8, sizeof(void *));
|
||||
if (cache == 0) {
|
||||
SpinMutexLock l(&internal_allocator_cache_mu);
|
||||
return internal_allocator()->Reallocate(&internal_allocator_cache, ptr,
|
||||
@@ -137,7 +137,8 @@ void InternalAllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
|
||||
}
|
||||
|
||||
// LowLevelAllocator
|
||||
constexpr uptr kLowLevelAllocatorDefaultAlignment = 8;
|
||||
constexpr usize kLowLevelAllocatorDefaultAlignment =
|
||||
Max<usize>(8, sizeof(void *));
|
||||
constexpr uptr kMinNumPagesRounded = 16;
|
||||
constexpr uptr kMinRoundedSize = 65536;
|
||||
static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define SANITIZER_ALLOCATOR_DLSYM_H
|
||||
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_checks.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
@@ -31,24 +33,22 @@ struct DlSymAllocator {
|
||||
UNLIKELY(internal_allocator()->FromPrimary(ptr));
|
||||
}
|
||||
|
||||
static void *Allocate(uptr size_in_bytes) {
|
||||
void *ptr = InternalAlloc(size_in_bytes, nullptr, kWordSize);
|
||||
static void *Allocate(uptr size_in_bytes, uptr align = kWordSize) {
|
||||
void *ptr = InternalAlloc(size_in_bytes, nullptr, align);
|
||||
CHECK(internal_allocator()->FromPrimary(ptr));
|
||||
Details::OnAllocate(ptr,
|
||||
internal_allocator()->GetActuallyAllocatedSize(ptr));
|
||||
Details::OnAllocate(ptr, GetSize(ptr));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *Callocate(SIZE_T nmemb, SIZE_T size) {
|
||||
static void *Callocate(usize nmemb, usize size) {
|
||||
void *ptr = InternalCalloc(nmemb, size);
|
||||
CHECK(internal_allocator()->FromPrimary(ptr));
|
||||
Details::OnAllocate(ptr,
|
||||
internal_allocator()->GetActuallyAllocatedSize(ptr));
|
||||
Details::OnAllocate(ptr, GetSize(ptr));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void Free(void *ptr) {
|
||||
uptr size = internal_allocator()->GetActuallyAllocatedSize(ptr);
|
||||
uptr size = GetSize(ptr);
|
||||
Details::OnFree(ptr, size);
|
||||
InternalFree(ptr);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ struct DlSymAllocator {
|
||||
Free(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
uptr size = internal_allocator()->GetActuallyAllocatedSize(ptr);
|
||||
uptr size = GetSize(ptr);
|
||||
uptr memcpy_size = Min(new_size, size);
|
||||
void *new_ptr = Allocate(new_size);
|
||||
if (new_ptr)
|
||||
@@ -70,6 +70,15 @@ struct DlSymAllocator {
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
static void *ReallocArray(void *ptr, uptr count, uptr size) {
|
||||
CHECK(!CheckForCallocOverflow(count, size));
|
||||
return Realloc(ptr, count * size);
|
||||
}
|
||||
|
||||
static uptr GetSize(void *ptr) {
|
||||
return internal_allocator()->GetActuallyAllocatedSize(ptr);
|
||||
}
|
||||
|
||||
static void OnAllocate(const void *ptr, uptr size) {}
|
||||
static void OnFree(const void *ptr, uptr size) {}
|
||||
};
|
||||
|
||||
@@ -185,9 +185,10 @@ class SizeClassAllocator64 {
|
||||
// recoverable.
|
||||
if (UNLIKELY(!EnsureFreeArraySpace(region, region_beg,
|
||||
new_num_freed_chunks))) {
|
||||
Report("FATAL: Internal error: %s's allocator exhausted the free list "
|
||||
"space for size class %zd (%zd bytes).\n", SanitizerToolName,
|
||||
class_id, ClassIdToSize(class_id));
|
||||
Report(
|
||||
"FATAL: Internal error: %s's allocator exhausted the free list "
|
||||
"space for size class %zu (%zu bytes).\n",
|
||||
SanitizerToolName, class_id, ClassIdToSize(class_id));
|
||||
Die();
|
||||
}
|
||||
for (uptr i = 0; i < n_chunks; i++)
|
||||
@@ -763,8 +764,9 @@ class SizeClassAllocator64 {
|
||||
if (!region->exhausted) {
|
||||
region->exhausted = true;
|
||||
Printf("%s: Out of memory. ", SanitizerToolName);
|
||||
Printf("The process has exhausted %zuMB for size class %zu.\n",
|
||||
kRegionSize >> 20, ClassIdToSize(class_id));
|
||||
Printf(
|
||||
"The process has exhausted %zu MB for size class %zu (%zu bytes).\n",
|
||||
kRegionSize >> 20, class_id, ClassIdToSize(class_id));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
27
lib/tsan/sanitizer_common/sanitizer_common.h
vendored
27
lib/tsan/sanitizer_common/sanitizer_common.h
vendored
@@ -83,8 +83,8 @@ int TgKill(pid_t pid, tid_t tid, int sig);
|
||||
uptr GetThreadSelf();
|
||||
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
||||
uptr *stack_bottom);
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
uptr *tls_addr, uptr *tls_size);
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
|
||||
uptr *tls_begin, uptr *tls_end);
|
||||
|
||||
// Memory management
|
||||
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false);
|
||||
@@ -239,13 +239,15 @@ void RemoveANSIEscapeSequencesFromString(char *buffer);
|
||||
void Printf(const char *format, ...) FORMAT(1, 2);
|
||||
void Report(const char *format, ...) FORMAT(1, 2);
|
||||
void SetPrintfAndReportCallback(void (*callback)(const char *));
|
||||
#define VReport(level, ...) \
|
||||
do { \
|
||||
if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \
|
||||
#define VReport(level, ...) \
|
||||
do { \
|
||||
if (UNLIKELY((uptr)Verbosity() >= (level))) \
|
||||
Report(__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define VPrintf(level, ...) \
|
||||
do { \
|
||||
if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \
|
||||
#define VPrintf(level, ...) \
|
||||
do { \
|
||||
if (UNLIKELY((uptr)Verbosity() >= (level))) \
|
||||
Printf(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
// Lock sanitizer error reporting and protects against nested errors.
|
||||
@@ -266,7 +268,15 @@ class ScopedErrorReportLock {
|
||||
extern uptr stoptheworld_tracer_pid;
|
||||
extern uptr stoptheworld_tracer_ppid;
|
||||
|
||||
// Returns true if the entire range can be read.
|
||||
bool IsAccessibleMemoryRange(uptr beg, uptr size);
|
||||
// Attempts to copy `n` bytes from memory range starting at `src` to `dest`.
|
||||
// Returns true if the entire range can be read. Returns `false` if any part of
|
||||
// the source range cannot be read, in which case the contents of `dest` are
|
||||
// undefined.
|
||||
bool TryMemCpy(void *dest, const void *src, uptr n);
|
||||
// Copies accessible memory, and zero fill inaccessible.
|
||||
void MemCpyAccessible(void *dest, const void *src, uptr n);
|
||||
|
||||
// Error report formatting.
|
||||
const char *StripPathPrefix(const char *filepath,
|
||||
@@ -917,7 +927,6 @@ typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
|
||||
|
||||
enum AndroidApiLevel {
|
||||
ANDROID_NOT_ANDROID = 0,
|
||||
ANDROID_KITKAT = 19,
|
||||
ANDROID_LOLLIPOP_MR1 = 22,
|
||||
ANDROID_POST_LOLLIPOP = 23
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "sanitizer_errno.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_platform_interceptors.h"
|
||||
#include "sanitizer_platform_limits_posix.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
#include "sanitizer_tls_get_addr.h"
|
||||
|
||||
@@ -127,6 +128,39 @@ extern const short *_toupper_tab_;
|
||||
extern const short *_tolower_tab_;
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX && SANITIZER_SPARC32
|
||||
// On 32-bit Linux/sparc64, double and long double are identical and glibc
|
||||
// uses a __nldbl_ (no long double) prefix for various stdio functions.
|
||||
# define __isoc23_fscanf __nldbl___isoc23_fscanf
|
||||
# define __isoc23_scanf __nldbl___isoc23_scanf
|
||||
# define __isoc23_sscanf __nldbl___isoc23_sscanf
|
||||
# define __isoc23_vfscanf __nldbl___isoc23_vfscanf
|
||||
# define __isoc23_vscanf __nldbl___isoc23_vscanf
|
||||
# define __isoc23_vsscanf __nldbl___isoc23_vsscanf
|
||||
# define __isoc99_fscanf __nldbl___isoc99_fscanf
|
||||
# define __isoc99_scanf __nldbl___isoc99_scanf
|
||||
# define __isoc99_sscanf __nldbl___isoc99_sscanf
|
||||
# define __isoc99_vfscanf __nldbl___isoc99_vfscanf
|
||||
# define __isoc99_vscanf __nldbl___isoc99_vscanf
|
||||
# define __isoc99_vsscanf __nldbl___isoc99_vsscanf
|
||||
# define asprintf __nldbl_asprintf
|
||||
# define fprintf __nldbl_fprintf
|
||||
# define fscanf __nldbl_fscanf
|
||||
# define printf __nldbl_printf
|
||||
# define scanf __nldbl_scanf
|
||||
# define snprintf __nldbl_snprintf
|
||||
# define sprintf __nldbl_sprintf
|
||||
# define sscanf __nldbl_sscanf
|
||||
# define vasprintf __nldbl_vasprintf
|
||||
# define vfprintf __nldbl_vfprintf
|
||||
# define vfscanf __nldbl_vfscanf
|
||||
# define vprintf __nldbl_vprintf
|
||||
# define vscanf __nldbl_vscanf
|
||||
# define vsnprintf __nldbl_vsnprintf
|
||||
# define vsprintf __nldbl_vsprintf
|
||||
# define vsscanf __nldbl_vsscanf
|
||||
#endif
|
||||
|
||||
#if SANITIZER_MUSL && \
|
||||
(defined(__i386__) || defined(__arm__) || SANITIZER_MIPS32 || SANITIZER_PPC32)
|
||||
// musl 1.2.0 on existing 32-bit architectures uses new symbol names for the
|
||||
@@ -313,7 +347,7 @@ extern const short *_tolower_tab_;
|
||||
uptr copy_length = internal_strnlen(s, size); \
|
||||
char *new_mem = (char *)WRAP(malloc)(copy_length + 1); \
|
||||
if (common_flags()->intercept_strndup) { \
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s, Min(size, copy_length + 1)); \
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s, Min<uptr>(size, copy_length + 1)); \
|
||||
} \
|
||||
if (new_mem) { \
|
||||
COMMON_INTERCEPTOR_COPY_STRING(ctx, new_mem, s, copy_length); \
|
||||
@@ -411,7 +445,7 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T maxlen) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRNDUP
|
||||
INTERCEPTOR(char*, strndup, const char *s, uptr size) {
|
||||
INTERCEPTOR(char*, strndup, const char *s, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size);
|
||||
}
|
||||
@@ -421,7 +455,7 @@ INTERCEPTOR(char*, strndup, const char *s, uptr size) {
|
||||
#endif // SANITIZER_INTERCEPT_STRNDUP
|
||||
|
||||
#if SANITIZER_INTERCEPT___STRNDUP
|
||||
INTERCEPTOR(char*, __strndup, const char *s, uptr size) {
|
||||
INTERCEPTOR(char*, __strndup, const char *s, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size);
|
||||
}
|
||||
@@ -477,23 +511,23 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
|
||||
}
|
||||
|
||||
DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc,
|
||||
const char *s1, const char *s2, uptr n,
|
||||
const char *s1, const char *s2, usize n,
|
||||
int result)
|
||||
|
||||
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
|
||||
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, usize size) {
|
||||
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
|
||||
return internal_strncmp(s1, s2, size);
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size);
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
uptr i;
|
||||
usize i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (c1 != c2 || c1 == '\0') break;
|
||||
}
|
||||
uptr i1 = i;
|
||||
uptr i2 = i;
|
||||
usize i1 = i;
|
||||
usize i2 = i;
|
||||
if (common_flags()->strict_string_checks) {
|
||||
for (; i1 < size && s1[i1]; i1++) {}
|
||||
for (; i2 < size && s2[i2]; i2++) {}
|
||||
@@ -542,21 +576,21 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
|
||||
}
|
||||
|
||||
DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncasecmp, uptr called_pc,
|
||||
const char *s1, const char *s2, uptr size,
|
||||
const char *s1, const char *s2, usize size,
|
||||
int result)
|
||||
|
||||
INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, size);
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
uptr i;
|
||||
usize i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
|
||||
}
|
||||
uptr i1 = i;
|
||||
uptr i2 = i;
|
||||
usize i1 = i;
|
||||
usize i2 = i;
|
||||
if (common_flags()->strict_string_checks) {
|
||||
for (; i1 < size && s1[i1]; i1++) {}
|
||||
for (; i2 < size && s2[i2]; i2++) {}
|
||||
@@ -799,13 +833,13 @@ INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
|
||||
|
||||
#if SANITIZER_INTERCEPT_MEMCMP
|
||||
DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc,
|
||||
const void *s1, const void *s2, uptr n,
|
||||
const void *s1, const void *s2, usize n,
|
||||
int result)
|
||||
|
||||
// Common code for `memcmp` and `bcmp`.
|
||||
int MemcmpInterceptorCommon(void *ctx,
|
||||
int (*real_fn)(const void *, const void *, uptr),
|
||||
const void *a1, const void *a2, uptr size) {
|
||||
int (*real_fn)(const void *, const void *, usize),
|
||||
const void *a1, const void *a2, usize size) {
|
||||
if (common_flags()->intercept_memcmp) {
|
||||
if (common_flags()->strict_memcmp) {
|
||||
// Check the entire regions even if the first bytes of the buffers are
|
||||
@@ -817,7 +851,7 @@ int MemcmpInterceptorCommon(void *ctx,
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
const unsigned char *s1 = (const unsigned char*)a1;
|
||||
const unsigned char *s2 = (const unsigned char*)a2;
|
||||
uptr i;
|
||||
usize i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = s1[i];
|
||||
c2 = s2[i];
|
||||
@@ -837,7 +871,7 @@ int MemcmpInterceptorCommon(void *ctx,
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
|
||||
INTERCEPTOR(int, memcmp, const void *a1, const void *a2, usize size) {
|
||||
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
|
||||
return internal_memcmp(a1, a2, size);
|
||||
void *ctx;
|
||||
@@ -851,7 +885,7 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_BCMP
|
||||
INTERCEPTOR(int, bcmp, const void *a1, const void *a2, uptr size) {
|
||||
INTERCEPTOR(int, bcmp, const void *a1, const void *a2, usize size) {
|
||||
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
|
||||
return internal_memcmp(a1, a2, size);
|
||||
void *ctx;
|
||||
@@ -1104,7 +1138,7 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_FWRITE
|
||||
INTERCEPTOR(SIZE_T, fwrite, const void *p, uptr size, uptr nmemb, void *file) {
|
||||
INTERCEPTOR(SIZE_T, fwrite, const void *p, usize size, usize nmemb, void *file) {
|
||||
// libc file streams can call user-supplied functions, see fopencookie.
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, fwrite, p, size, nmemb, file);
|
||||
@@ -1255,6 +1289,12 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
|
||||
static const int PR_SET_VMA = 0x53564d41;
|
||||
static const int PR_SCHED_CORE = 62;
|
||||
static const int PR_SCHED_CORE_GET = 0;
|
||||
static const int PR_GET_PDEATHSIG = 2;
|
||||
|
||||
# if !SANITIZER_ANDROID
|
||||
static const int PR_SET_SECCOMP = 22;
|
||||
static const int SECCOMP_MODE_FILTER = 2;
|
||||
# endif
|
||||
if (option == PR_SET_VMA && arg2 == 0UL) {
|
||||
char *name = (char *)arg5;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, internal_strlen(name) + 1);
|
||||
@@ -1270,7 +1310,14 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, internal_strlen(name) + 1);
|
||||
} else if (res != -1 && option == PR_SCHED_CORE &&
|
||||
arg2 == PR_SCHED_CORE_GET) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64*)(arg5), sizeof(u64));
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg5), sizeof(u64));
|
||||
} else if (res != -1 && option == PR_GET_PDEATHSIG) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg2), sizeof(int));
|
||||
# if SANITIZER_GLIBC
|
||||
} else if (res != -1 && option == PR_SET_SECCOMP &&
|
||||
arg2 == SECCOMP_MODE_FILTER) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg3), struct_sock_fprog_sz);
|
||||
# endif
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -2242,6 +2289,61 @@ INTERCEPTOR(int, pthread_getcpuclockid, uptr thread,
|
||||
#define INIT_CLOCK_GETCPUCLOCKID
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_TIMER_CREATE
|
||||
INTERCEPTOR(int, timer_create, __sanitizer_clockid_t clockid, void *sevp,
|
||||
__sanitizer_timer_t *timer) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, timer_create, clockid, sevp, timer);
|
||||
int res = REAL(timer_create)(clockid, sevp, timer);
|
||||
if (!res && timer) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, timer, sizeof *timer);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, timer_delete, __sanitizer_timer_t timer) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, timer_delete, timer);
|
||||
int res = REAL(timer_delete)(timer);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, timer_gettime, __sanitizer_timer_t timer,
|
||||
struct __sanitizer_itimerspec *curr_value) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, timer_gettime, timer, curr_value);
|
||||
int res = REAL(timer_gettime)(timer, curr_value);
|
||||
if (!res && curr_value) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, sizeof *curr_value);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, timer_settime, __sanitizer_timer_t timer, int flags,
|
||||
const struct __sanitizer_itimerspec *new_value,
|
||||
struct __sanitizer_itimerspec *old_value) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, timer_settime, timer, flags, new_value,
|
||||
old_value);
|
||||
int res = REAL(timer_settime)(timer, flags, new_value, old_value);
|
||||
if (!res) {
|
||||
if (new_value)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, sizeof *new_value);
|
||||
if (old_value)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, sizeof *old_value);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
# define INIT_TIMER_CREATE \
|
||||
COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_create, "GLIBC_2.3.3"); \
|
||||
COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_delete, "GLIBC_2.3.3"); \
|
||||
COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_gettime, "GLIBC_2.3.3"); \
|
||||
COMMON_INTERCEPT_FUNCTION_GLIBC_VER_MIN(timer_settime, "GLIBC_2.3.3");
|
||||
#else
|
||||
# define INIT_TIMER_CREATE
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_GETITIMER
|
||||
INTERCEPTOR(int, getitimer, int which, void *curr_value) {
|
||||
void *ctx;
|
||||
@@ -2287,6 +2389,25 @@ INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) {
|
||||
#define INIT_GETITIMER
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_TIMESPEC_GET
|
||||
INTERCEPTOR(int, timespec_get, struct __sanitizer_timespec *ts, int base) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, timespec_get, ts, base);
|
||||
// We don't yet know if ts is addressable, so we use our own scratch buffer
|
||||
struct __sanitizer_timespec ts_local;
|
||||
int res = REAL(timespec_get)(&ts_local, base);
|
||||
if (res) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ts,
|
||||
sizeof(struct __sanitizer_timespec));
|
||||
internal_memcpy(ts, &ts_local, sizeof(struct __sanitizer_timespec));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
# define INIT_TIMESPEC_GET COMMON_INTERCEPT_FUNCTION(timespec_get);
|
||||
#else
|
||||
# define INIT_TIMESPEC_GET
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_GLOB
|
||||
static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob));
|
||||
@@ -3427,23 +3548,27 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data);
|
||||
__sanitizer_iovec local_iovec;
|
||||
|
||||
if (data) {
|
||||
void *data_arg = ptrace_data_arg(request, addr, data);
|
||||
if (data_arg) {
|
||||
if (request == ptrace_setregs) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, struct_user_regs_struct_sz);
|
||||
} else if (request == ptrace_setfpregs) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg,
|
||||
struct_user_fpregs_struct_sz);
|
||||
} else if (request == ptrace_setfpxregs) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg,
|
||||
struct_user_fpxregs_struct_sz);
|
||||
} else if (request == ptrace_setvfpregs) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg,
|
||||
struct_user_vfpregs_struct_sz);
|
||||
} else if (request == ptrace_setsiginfo) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, siginfo_t_sz);
|
||||
|
||||
// Some kernel might zero the iovec::iov_base in case of invalid
|
||||
// write access. In this case copy the invalid address for further
|
||||
// inspection.
|
||||
// Some kernel might zero the iovec::iov_base in case of invalid
|
||||
// write access. In this case copy the invalid address for further
|
||||
// inspection.
|
||||
} else if (request == ptrace_setregset || request == ptrace_getregset) {
|
||||
__sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
|
||||
__sanitizer_iovec *iovec = (__sanitizer_iovec *)data_arg;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec));
|
||||
local_iovec = *iovec;
|
||||
if (request == ptrace_setregset)
|
||||
@@ -3456,23 +3581,26 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
uptr res = REAL(ptrace)(request, pid, addr, data);
|
||||
|
||||
if (!res && data) {
|
||||
if (!res && data_arg) {
|
||||
// Note that PEEK* requests assign different meaning to the return value.
|
||||
// This function does not handle them (nor does it need to).
|
||||
if (request == ptrace_getregs) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, struct_user_regs_struct_sz);
|
||||
} else if (request == ptrace_getfpregs) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg,
|
||||
struct_user_fpregs_struct_sz);
|
||||
} else if (request == ptrace_getfpxregs) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg,
|
||||
struct_user_fpxregs_struct_sz);
|
||||
} else if (request == ptrace_getvfpregs) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg,
|
||||
struct_user_vfpregs_struct_sz);
|
||||
} else if (request == ptrace_getsiginfo) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, siginfo_t_sz);
|
||||
} else if (request == ptrace_geteventmsg) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long));
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, sizeof(unsigned long));
|
||||
} else if (request == ptrace_getregset) {
|
||||
__sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
|
||||
__sanitizer_iovec *iovec = (__sanitizer_iovec *)data_arg;
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec));
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base,
|
||||
local_iovec.iov_len);
|
||||
@@ -6425,12 +6553,12 @@ static void MlockIsUnsupported() {
|
||||
SanitizerToolName);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, mlock, const void *addr, uptr len) {
|
||||
INTERCEPTOR(int, mlock, const void *addr, usize len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, munlock, const void *addr, uptr len) {
|
||||
INTERCEPTOR(int, munlock, const void *addr, usize len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
@@ -8823,83 +8951,6 @@ INTERCEPTOR(char *, RMD160Data, u8 *data, SIZE_T len, char *buf) {
|
||||
#define INIT_RMD160
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_MD5
|
||||
INTERCEPTOR(void, MD5Init, void *context) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, MD5Init, context);
|
||||
REAL(MD5Init)(context);
|
||||
if (context)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, MD5Update, void *context, const unsigned char *data,
|
||||
unsigned int len) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, MD5Update, context, data, len);
|
||||
if (data && len > 0)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
|
||||
if (context)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
|
||||
REAL(MD5Update)(context, data, len);
|
||||
if (context)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, MD5Final, unsigned char digest[16], void *context) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, MD5Final, digest, context);
|
||||
if (context)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
|
||||
REAL(MD5Final)(digest, context);
|
||||
if (digest)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
|
||||
}
|
||||
|
||||
INTERCEPTOR(char *, MD5End, void *context, char *buf) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, MD5End, context, buf);
|
||||
if (context)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
|
||||
char *ret = REAL(MD5End)(context, buf);
|
||||
if (ret)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
INTERCEPTOR(char *, MD5File, const char *filename, char *buf) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, MD5File, filename, buf);
|
||||
if (filename)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);
|
||||
char *ret = REAL(MD5File)(filename, buf);
|
||||
if (ret)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
INTERCEPTOR(char *, MD5Data, const unsigned char *data, unsigned int len,
|
||||
char *buf) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, MD5Data, data, len, buf);
|
||||
if (data && len > 0)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
|
||||
char *ret = REAL(MD5Data)(data, len, buf);
|
||||
if (ret)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define INIT_MD5 \
|
||||
COMMON_INTERCEPT_FUNCTION(MD5Init); \
|
||||
COMMON_INTERCEPT_FUNCTION(MD5Update); \
|
||||
COMMON_INTERCEPT_FUNCTION(MD5Final); \
|
||||
COMMON_INTERCEPT_FUNCTION(MD5End); \
|
||||
COMMON_INTERCEPT_FUNCTION(MD5File); \
|
||||
COMMON_INTERCEPT_FUNCTION(MD5Data)
|
||||
#else
|
||||
#define INIT_MD5
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_FSEEK
|
||||
INTERCEPTOR(int, fseek, __sanitizer_FILE *stream, long int offset, int whence) {
|
||||
void *ctx;
|
||||
@@ -9030,107 +9081,6 @@ INTERCEPTOR(char *, MD2Data, const unsigned char *data, unsigned int len,
|
||||
#define INIT_MD2
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_SHA2
|
||||
#define SHA2_INTERCEPTORS(LEN, SHA2_STATE_T) \
|
||||
INTERCEPTOR(void, SHA##LEN##_Init, void *context) { \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Init, context); \
|
||||
REAL(SHA##LEN##_Init)(context); \
|
||||
if (context) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
|
||||
} \
|
||||
INTERCEPTOR(void, SHA##LEN##_Update, void *context, \
|
||||
const u8 *data, SIZE_T len) { \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Update, context, data, len); \
|
||||
if (data && len > 0) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
|
||||
if (context) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
|
||||
REAL(SHA##LEN##_Update)(context, data, len); \
|
||||
if (context) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
|
||||
} \
|
||||
INTERCEPTOR(void, SHA##LEN##_Final, u8 digest[LEN/8], \
|
||||
void *context) { \
|
||||
void *ctx; \
|
||||
CHECK_EQ(SHA##LEN##_digest_length, LEN/8); \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Final, digest, context); \
|
||||
if (context) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
|
||||
REAL(SHA##LEN##_Final)(digest, context); \
|
||||
if (digest) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, \
|
||||
sizeof(digest[0]) * \
|
||||
SHA##LEN##_digest_length); \
|
||||
} \
|
||||
INTERCEPTOR(char *, SHA##LEN##_End, void *context, char *buf) { \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_End, context, buf); \
|
||||
if (context) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
|
||||
char *ret = REAL(SHA##LEN##_End)(context, buf); \
|
||||
if (ret) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
|
||||
return ret; \
|
||||
} \
|
||||
INTERCEPTOR(char *, SHA##LEN##_File, const char *filename, char *buf) { \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_File, filename, buf); \
|
||||
if (filename) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);\
|
||||
char *ret = REAL(SHA##LEN##_File)(filename, buf); \
|
||||
if (ret) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
|
||||
return ret; \
|
||||
} \
|
||||
INTERCEPTOR(char *, SHA##LEN##_FileChunk, const char *filename, char *buf, \
|
||||
OFF_T offset, OFF_T length) { \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_FileChunk, filename, buf, offset, \
|
||||
length); \
|
||||
if (filename) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);\
|
||||
char *ret = REAL(SHA##LEN##_FileChunk)(filename, buf, offset, length); \
|
||||
if (ret) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
|
||||
return ret; \
|
||||
} \
|
||||
INTERCEPTOR(char *, SHA##LEN##_Data, u8 *data, SIZE_T len, char *buf) { \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Data, data, len, buf); \
|
||||
if (data && len > 0) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
|
||||
char *ret = REAL(SHA##LEN##_Data)(data, len, buf); \
|
||||
if (ret) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
SHA2_INTERCEPTORS(224, u32)
|
||||
SHA2_INTERCEPTORS(256, u32)
|
||||
SHA2_INTERCEPTORS(384, u64)
|
||||
SHA2_INTERCEPTORS(512, u64)
|
||||
|
||||
#define INIT_SHA2_INTECEPTORS(LEN) \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Init); \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Update); \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Final); \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_End); \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_File); \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_FileChunk); \
|
||||
COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Data)
|
||||
|
||||
#define INIT_SHA2 \
|
||||
INIT_SHA2_INTECEPTORS(224); \
|
||||
INIT_SHA2_INTECEPTORS(256); \
|
||||
INIT_SHA2_INTECEPTORS(384); \
|
||||
INIT_SHA2_INTECEPTORS(512)
|
||||
#undef SHA2_INTERCEPTORS
|
||||
#else
|
||||
#define INIT_SHA2
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_VIS
|
||||
INTERCEPTOR(char *, vis, char *dst, int c, int flag, int nextc) {
|
||||
void *ctx;
|
||||
@@ -9980,7 +9930,7 @@ INTERCEPTOR(SSIZE_T, getrandom, void *buf, SIZE_T buflen, unsigned int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getrandom, buf, buflen, flags);
|
||||
// If GRND_NONBLOCK is set in the flags, it is non blocking.
|
||||
static const int grnd_nonblock = 1;
|
||||
static const int grnd_nonblock = 1;
|
||||
SSIZE_T n;
|
||||
if ((flags & grnd_nonblock))
|
||||
n = REAL(getrandom)(buf, buflen, flags);
|
||||
@@ -10296,6 +10246,23 @@ INTERCEPTOR(SSIZE_T, pwritev2, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
#define INIT_PWRITEV2
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_FREADLINK
|
||||
INTERCEPTOR(SSIZE_T, freadlink, int fd, char *buf, SIZE_T bufsiz) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, freadlink, fd, buf, bufsiz);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = REAL(freadlink)(fd, buf, bufsiz);
|
||||
if (res > 0)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res);
|
||||
if (res >= 0 && fd > 0)
|
||||
COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
}
|
||||
# define INIT_FREADLINK COMMON_INTERCEPT_FUNCTION(freadlink)
|
||||
#else
|
||||
# define INIT_FREADLINK
|
||||
#endif
|
||||
|
||||
#include "sanitizer_common_interceptors_netbsd_compat.inc"
|
||||
|
||||
namespace __sanitizer {
|
||||
@@ -10373,8 +10340,10 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_SETPWENT;
|
||||
INIT_CLOCK_GETTIME;
|
||||
INIT_CLOCK_GETCPUCLOCKID;
|
||||
INIT_TIMER_CREATE;
|
||||
INIT_GETITIMER;
|
||||
INIT_TIME;
|
||||
INIT_TIMESPEC_GET;
|
||||
INIT_GLOB;
|
||||
INIT_GLOB64;
|
||||
INIT___B64_TO;
|
||||
@@ -10588,10 +10557,8 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_SHA1;
|
||||
INIT_MD4;
|
||||
INIT_RMD160;
|
||||
INIT_MD5;
|
||||
INIT_FSEEK;
|
||||
INIT_MD2;
|
||||
INIT_SHA2;
|
||||
INIT_VIS;
|
||||
INIT_CDB;
|
||||
INIT_GETFSENT;
|
||||
@@ -10617,6 +10584,7 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_CPUSET_GETAFFINITY;
|
||||
INIT_PREADV2;
|
||||
INIT_PWRITEV2;
|
||||
INIT_FREADLINK;
|
||||
|
||||
INIT___PRINTF_CHK;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_MEMSET
|
||||
INTERCEPTOR(void *, memset, void *dst, int v, uptr size) {
|
||||
INTERCEPTOR(void *, memset, void *dst, int v, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size);
|
||||
}
|
||||
@@ -93,7 +93,7 @@ INTERCEPTOR(void *, memset, void *dst, int v, uptr size) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_MEMMOVE
|
||||
INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) {
|
||||
INTERCEPTOR(void *, memmove, void *dst, const void *src, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_MEMCPY
|
||||
INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) {
|
||||
INTERCEPTOR(void *, memcpy, void *dst, const void *src, usize size) {
|
||||
// On OS X, calling internal_memcpy here will cause memory corruptions,
|
||||
// because memcpy and memmove are actually aliases of the same
|
||||
// implementation. We need to use internal_memmove here.
|
||||
@@ -133,63 +133,63 @@ INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_AEABI_MEM
|
||||
INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
// Note the argument order.
|
||||
INTERCEPTOR(void *, __aeabi_memset, void *block, uptr size, int c) {
|
||||
INTERCEPTOR(void *, __aeabi_memset, void *block, usize size, int c) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memset4, void *block, uptr size, int c) {
|
||||
INTERCEPTOR(void *, __aeabi_memset4, void *block, usize size, int c) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memset8, void *block, uptr size, int c) {
|
||||
INTERCEPTOR(void *, __aeabi_memset8, void *block, usize size, int c) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memclr, void *block, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memclr, void *block, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memclr4, void *block, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memclr4, void *block, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) {
|
||||
INTERCEPTOR(void *, __aeabi_memclr8, void *block, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) {
|
||||
#endif // SANITIZER_INTERCEPT_AEABI_MEM
|
||||
|
||||
#if SANITIZER_INTERCEPT___BZERO
|
||||
INTERCEPTOR(void *, __bzero, void *block, uptr size) {
|
||||
INTERCEPTOR(void *, __bzero, void *block, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
|
||||
}
|
||||
@@ -222,7 +222,7 @@ INTERCEPTOR(void *, __bzero, void *block, uptr size) {
|
||||
#endif // SANITIZER_INTERCEPT___BZERO
|
||||
|
||||
#if SANITIZER_INTERCEPT_BZERO
|
||||
INTERCEPTOR(void *, bzero, void *block, uptr size) {
|
||||
INTERCEPTOR(void *, bzero, void *block, usize size) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
|
||||
}
|
||||
|
||||
48
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S
vendored
Normal file
48
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
#if defined(__aarch64__) && defined(__linux__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
#include "builtins/assembly.h"
|
||||
|
||||
ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA)
|
||||
|
||||
.comm _ZN14__interception10real_vforkE,8,8
|
||||
.globl ASM_WRAPPER_NAME(vfork)
|
||||
ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
|
||||
ASM_WRAPPER_NAME(vfork):
|
||||
// Save x30 in the off-stack spill area.
|
||||
hint #25 // paciasp
|
||||
stp xzr, x30, [sp, #-16]!
|
||||
bl COMMON_INTERCEPTOR_SPILL_AREA
|
||||
ldp xzr, x30, [sp], 16
|
||||
str x30, [x0]
|
||||
|
||||
// Call real vfork. This may return twice. User code that runs between the first and the second return
|
||||
// may clobber the stack frame of the interceptor; that's why it does not have a frame.
|
||||
adrp x0, _ZN14__interception10real_vforkE
|
||||
ldr x0, [x0, :lo12:_ZN14__interception10real_vforkE]
|
||||
blr x0
|
||||
|
||||
stp x0, xzr, [sp, #-16]!
|
||||
cmp x0, #0
|
||||
b.eq .L_exit
|
||||
|
||||
// x0 != 0 => parent process. Clear stack shadow.
|
||||
add x0, sp, #16
|
||||
bl COMMON_INTERCEPTOR_HANDLE_VFORK
|
||||
|
||||
.L_exit:
|
||||
// Restore x30.
|
||||
bl COMMON_INTERCEPTOR_SPILL_AREA
|
||||
ldr x30, [x0]
|
||||
ldp x0, xzr, [sp], 16
|
||||
hint #29 // autiasp
|
||||
|
||||
ret
|
||||
ASM_SIZE(vfork)
|
||||
|
||||
ASM_INTERCEPTOR_TRAMPOLINE(vfork)
|
||||
ASM_TRAMPOLINE_ALIAS(vfork, vfork)
|
||||
|
||||
GNU_PROPERTY_BTI_PAC
|
||||
|
||||
#endif
|
||||
49
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_arm.inc.S
vendored
Normal file
49
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_arm.inc.S
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
#if defined(__arm__) && defined(__linux__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
|
||||
ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA)
|
||||
|
||||
.comm _ZN14__interception10real_vforkE,4,4
|
||||
.globl ASM_WRAPPER_NAME(vfork)
|
||||
ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
|
||||
ASM_WRAPPER_NAME(vfork):
|
||||
// Save LR in the off-stack spill area.
|
||||
push {r4, lr}
|
||||
bl COMMON_INTERCEPTOR_SPILL_AREA
|
||||
pop {r4, lr}
|
||||
str lr, [r0]
|
||||
|
||||
// Call real vfork. This may return twice. User code that runs between the first and the second return
|
||||
// may clobber the stack frame of the interceptor; that's why it does not have a frame.
|
||||
ldr r0, .LCPI0_0
|
||||
.LPC0_0:
|
||||
ldr r0, [pc, r0]
|
||||
mov lr, pc
|
||||
bx r0
|
||||
|
||||
push {r0, r4}
|
||||
cmp r0, #0
|
||||
beq .L_exit
|
||||
|
||||
// r0 != 0 => parent process. Clear stack shadow.
|
||||
add r0, sp, #8
|
||||
bl COMMON_INTERCEPTOR_HANDLE_VFORK
|
||||
|
||||
.L_exit:
|
||||
// Restore LR.
|
||||
bl COMMON_INTERCEPTOR_SPILL_AREA
|
||||
ldr lr, [r0]
|
||||
pop {r0, r4}
|
||||
|
||||
mov pc, lr
|
||||
|
||||
.LCPI0_0:
|
||||
.long _ZN14__interception10real_vforkE - (.LPC0_0+8)
|
||||
|
||||
ASM_SIZE(vfork)
|
||||
|
||||
ASM_INTERCEPTOR_TRAMPOLINE(vfork)
|
||||
ASM_TRAMPOLINE_ALIAS(vfork, vfork)
|
||||
|
||||
#endif
|
||||
64
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_i386.inc.S
vendored
Normal file
64
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_i386.inc.S
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
#if defined(__i386__) && defined(__linux__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
|
||||
.comm _ZN14__interception10real_vforkE,4,4
|
||||
.globl ASM_WRAPPER_NAME(vfork)
|
||||
ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
|
||||
ASM_WRAPPER_NAME(vfork):
|
||||
_CET_ENDBR
|
||||
// Store return address in the spill area and tear down the stack frame.
|
||||
sub $12, %esp
|
||||
call COMMON_INTERCEPTOR_SPILL_AREA
|
||||
mov 12(%esp), %ecx
|
||||
mov %ecx, (%eax)
|
||||
add $16, %esp
|
||||
|
||||
call .L0$pb
|
||||
.L0$pb:
|
||||
pop %eax
|
||||
.Ltmp0:
|
||||
add $_GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb), %eax
|
||||
call *_ZN14__interception10real_vforkE@GOTOFF(%eax)
|
||||
|
||||
// Restore the stack frame.
|
||||
// 12(%esp) return address
|
||||
// 8(%esp) spill %ebx
|
||||
// 4(%esp) spill REAL(vfork) return value
|
||||
// (%esp) call frame (arg0) for __*_handle_vfork
|
||||
sub $16, %esp
|
||||
mov %ebx, 8(%esp)
|
||||
mov %eax, 4(%esp)
|
||||
|
||||
// Form GOT address in %ebx.
|
||||
call .L1$pb
|
||||
.L1$pb:
|
||||
pop %ebx
|
||||
.Ltmp1:
|
||||
add $_GLOBAL_OFFSET_TABLE_+(.Ltmp1-.L1$pb), %ebx
|
||||
|
||||
// Restore original return address.
|
||||
call COMMON_INTERCEPTOR_SPILL_AREA
|
||||
mov (%eax), %ecx
|
||||
mov %ecx, 12(%esp)
|
||||
mov 4(%esp), %eax
|
||||
|
||||
// Call handle_vfork in the parent process (%rax != 0).
|
||||
test %eax, %eax
|
||||
je .L_exit
|
||||
|
||||
lea 16(%esp), %ecx
|
||||
mov %ecx, (%esp)
|
||||
call COMMON_INTERCEPTOR_HANDLE_VFORK@PLT
|
||||
|
||||
.L_exit:
|
||||
mov 4(%esp), %eax
|
||||
mov 8(%esp), %ebx
|
||||
add $12, %esp
|
||||
ret
|
||||
ASM_SIZE(vfork)
|
||||
|
||||
ASM_INTERCEPTOR_TRAMPOLINE(vfork)
|
||||
ASM_TRAMPOLINE_ALIAS(vfork, vfork)
|
||||
|
||||
#endif
|
||||
57
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S
vendored
Normal file
57
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_loongarch64.inc.S
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
#if defined(__loongarch_lp64) && defined(__linux__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
|
||||
ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA)
|
||||
ASM_HIDDEN(_ZN14__interception10real_vforkE)
|
||||
|
||||
.text
|
||||
.globl ASM_WRAPPER_NAME(vfork)
|
||||
ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
|
||||
ASM_WRAPPER_NAME(vfork):
|
||||
// Save ra in the off-stack spill area.
|
||||
// allocate space on stack
|
||||
addi.d $sp, $sp, -16
|
||||
// store $ra value
|
||||
st.d $ra, $sp, 8
|
||||
bl COMMON_INTERCEPTOR_SPILL_AREA
|
||||
// restore previous values from stack
|
||||
ld.d $ra, $sp, 8
|
||||
// adjust stack
|
||||
addi.d $sp, $sp, 16
|
||||
// store $ra by $a0
|
||||
st.d $ra, $a0, 0
|
||||
|
||||
// Call real vfork. This may return twice. User code that runs between the first and the second return
|
||||
// may clobber the stack frame of the interceptor; that's why it does not have a frame.
|
||||
la.local $a0, _ZN14__interception10real_vforkE
|
||||
ld.d $a0, $a0, 0
|
||||
jirl $ra, $a0, 0
|
||||
|
||||
// adjust stack
|
||||
addi.d $sp, $sp, -16
|
||||
// store $a0 by adjusted stack
|
||||
st.d $a0, $sp, 8
|
||||
// jump to exit label if $a0 is 0
|
||||
beqz $a0, .L_exit
|
||||
|
||||
// $a0 != 0 => parent process. Clear stack shadow.
|
||||
// put old $sp to $a0
|
||||
addi.d $a0, $sp, 16
|
||||
bl %plt(COMMON_INTERCEPTOR_HANDLE_VFORK)
|
||||
|
||||
.L_exit:
|
||||
// Restore $ra
|
||||
bl COMMON_INTERCEPTOR_SPILL_AREA
|
||||
ld.d $ra, $a0, 0
|
||||
// load value by stack
|
||||
ld.d $a0, $sp, 8
|
||||
// adjust stack
|
||||
addi.d $sp, $sp, 16
|
||||
jr $ra
|
||||
ASM_SIZE(vfork)
|
||||
|
||||
ASM_INTERCEPTOR_TRAMPOLINE(vfork)
|
||||
ASM_TRAMPOLINE_ALIAS(vfork, vfork)
|
||||
|
||||
#endif
|
||||
56
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_riscv64.inc.S
vendored
Normal file
56
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_riscv64.inc.S
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
#if (defined(__riscv) && (__riscv_xlen == 64)) && defined(__linux__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
|
||||
ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA)
|
||||
|
||||
.comm _ZN14__interception10real_vforkE,8,8
|
||||
.globl ASM_WRAPPER_NAME(vfork)
|
||||
ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
|
||||
ASM_WRAPPER_NAME(vfork):
|
||||
// Save ra in the off-stack spill area.
|
||||
// allocate space on stack
|
||||
addi sp, sp, -16
|
||||
// store ra value
|
||||
sd ra, 8(sp)
|
||||
call COMMON_INTERCEPTOR_SPILL_AREA
|
||||
// restore previous values from stack
|
||||
ld ra, 8(sp)
|
||||
// adjust stack
|
||||
addi sp, sp, 16
|
||||
// store ra by x10
|
||||
sd ra, 0(x10)
|
||||
|
||||
// Call real vfork. This may return twice. User code that runs between the first and the second return
|
||||
// may clobber the stack frame of the interceptor; that's why it does not have a frame.
|
||||
la x10, _ZN14__interception10real_vforkE
|
||||
ld x10, 0(x10)
|
||||
jalr x10
|
||||
|
||||
// adjust stack
|
||||
addi sp, sp, -16
|
||||
// store x10 by adjusted stack
|
||||
sd x10, 8(sp)
|
||||
// jump to exit label if x10 is 0
|
||||
beqz x10, .L_exit
|
||||
|
||||
// x0 != 0 => parent process. Clear stack shadow.
|
||||
// put old sp to x10
|
||||
addi x10, sp, 16
|
||||
call COMMON_INTERCEPTOR_HANDLE_VFORK
|
||||
|
||||
.L_exit:
|
||||
// Restore ra
|
||||
call COMMON_INTERCEPTOR_SPILL_AREA
|
||||
ld ra, 0(x10)
|
||||
// load value by stack
|
||||
ld x10, 8(sp)
|
||||
// adjust stack
|
||||
addi sp, sp, 16
|
||||
ret
|
||||
ASM_SIZE(vfork)
|
||||
|
||||
ASM_INTERCEPTOR_TRAMPOLINE(vfork)
|
||||
ASM_TRAMPOLINE_ALIAS(vfork, vfork)
|
||||
|
||||
#endif
|
||||
42
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S
vendored
Normal file
42
lib/tsan/sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
#if defined(__x86_64__) && defined(__linux__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
|
||||
.comm _ZN14__interception10real_vforkE,8,8
|
||||
.globl ASM_WRAPPER_NAME(vfork)
|
||||
ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
|
||||
ASM_WRAPPER_NAME(vfork):
|
||||
_CET_ENDBR
|
||||
// Store return address in the spill area and tear down the stack frame.
|
||||
push %rcx
|
||||
call COMMON_INTERCEPTOR_SPILL_AREA
|
||||
pop %rcx
|
||||
pop %rdi
|
||||
mov %rdi, (%rax)
|
||||
|
||||
call *_ZN14__interception10real_vforkE(%rip)
|
||||
|
||||
// Restore return address from the spill area.
|
||||
push %rcx
|
||||
push %rax
|
||||
call COMMON_INTERCEPTOR_SPILL_AREA
|
||||
mov (%rax), %rdx
|
||||
mov %rdx, 8(%rsp)
|
||||
mov (%rsp), %rax
|
||||
|
||||
// Call handle_vfork in the parent process (%rax != 0).
|
||||
test %rax, %rax
|
||||
je .L_exit
|
||||
|
||||
lea 16(%rsp), %rdi
|
||||
call COMMON_INTERCEPTOR_HANDLE_VFORK@PLT
|
||||
|
||||
.L_exit:
|
||||
pop %rax
|
||||
ret
|
||||
ASM_SIZE(ASM_WRAPPER_NAME(vfork))
|
||||
|
||||
ASM_INTERCEPTOR_TRAMPOLINE(vfork)
|
||||
ASM_TRAMPOLINE_ALIAS(vfork, vfork)
|
||||
|
||||
#endif
|
||||
@@ -10,6 +10,7 @@
|
||||
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)
|
||||
@@ -22,6 +23,7 @@ INTERFACE_FUNCTION(__sanitizer_verify_double_ended_contiguous_container)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_on_print)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_get_dtls_size)
|
||||
// Sanitizer weak hooks
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_memcmp)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strcmp)
|
||||
@@ -51,3 +53,9 @@ INTERFACE_WEAK_FUNCTION(__sanitizer_ignore_free_hook)
|
||||
INTERFACE_FUNCTION(__sanitizer_internal_memcpy)
|
||||
INTERFACE_FUNCTION(__sanitizer_internal_memmove)
|
||||
INTERFACE_FUNCTION(__sanitizer_internal_memset)
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
INTERFACE_FUNCTION(__sanitizer_override_function)
|
||||
INTERFACE_FUNCTION(__sanitizer_override_function_by_addr)
|
||||
INTERFACE_FUNCTION(__sanitizer_register_weak_function)
|
||||
#endif
|
||||
|
||||
@@ -171,7 +171,7 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name,
|
||||
"ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
|
||||
"Perhaps you're using ulimit -v or ulimit -d\n",
|
||||
size);
|
||||
Abort();
|
||||
Die();
|
||||
}
|
||||
if (madvise_shadow && common_flags()->use_madv_dontdump)
|
||||
DontDumpShadowMemory(beg, size);
|
||||
@@ -219,6 +219,32 @@ static void StopStackDepotBackgroundThread() {
|
||||
static void StopStackDepotBackgroundThread() {}
|
||||
#endif
|
||||
|
||||
void MemCpyAccessible(void *dest, const void *src, uptr n) {
|
||||
if (TryMemCpy(dest, src, n))
|
||||
return;
|
||||
|
||||
const uptr page_size = GetPageSize();
|
||||
uptr b = reinterpret_cast<uptr>(src);
|
||||
uptr b_up = RoundUpTo(b, page_size);
|
||||
|
||||
uptr e = reinterpret_cast<uptr>(src) + n;
|
||||
uptr e_down = RoundDownTo(e, page_size);
|
||||
|
||||
auto copy_or_zero = [dest, src](uptr beg, uptr end) {
|
||||
const uptr udest = reinterpret_cast<uptr>(dest);
|
||||
const uptr usrc = reinterpret_cast<uptr>(src);
|
||||
void *d = reinterpret_cast<void *>(udest + (beg - usrc));
|
||||
const uptr size = end - beg;
|
||||
if (!TryMemCpy(d, reinterpret_cast<void *>(beg), size))
|
||||
internal_memset(d, 0, size);
|
||||
};
|
||||
|
||||
copy_or_zero(b, b_up);
|
||||
for (uptr p = b_up; p < e_down; p += page_size)
|
||||
copy_or_zero(p, p + page_size);
|
||||
copy_or_zero(e_down, e);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
|
||||
|
||||
@@ -20,13 +20,14 @@ namespace __sanitizer {
|
||||
// The Windows implementations of these functions use the win32 API directly,
|
||||
// bypassing libc.
|
||||
#if !SANITIZER_WINDOWS
|
||||
#if SANITIZER_LINUX
|
||||
# if SANITIZER_LINUX
|
||||
void LogMessageOnPrintf(const char *str) {}
|
||||
#endif
|
||||
void InitTlsSize() {}
|
||||
# endif
|
||||
void WriteToSyslog(const char *buffer) {}
|
||||
void Abort() { internal__exit(1); }
|
||||
bool CreateDir(const char *pathname) { return false; }
|
||||
#endif // !SANITIZER_WINDOWS
|
||||
#endif // !SANITIZER_WINDOWS
|
||||
|
||||
#if !SANITIZER_WINDOWS && !SANITIZER_APPLE
|
||||
void ListOfModules::init() {}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#if SANITIZER_LINUX
|
||||
|
||||
# include "sanitizer_libc.h"
|
||||
# include "sanitizer_platform_limits_posix.h"
|
||||
|
||||
# define PRE_SYSCALL(name) \
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name
|
||||
@@ -2530,18 +2531,19 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
|
||||
# if !SANITIZER_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \
|
||||
defined(__loongarch__) || SANITIZER_RISCV64)
|
||||
if (data) {
|
||||
defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__))
|
||||
long data_arg = ptrace_data_arg(request, addr, data);
|
||||
if (data_arg) {
|
||||
if (request == ptrace_setregs) {
|
||||
PRE_READ((void *)data, struct_user_regs_struct_sz);
|
||||
PRE_READ((void *)data_arg, struct_user_regs_struct_sz);
|
||||
} else if (request == ptrace_setfpregs) {
|
||||
PRE_READ((void *)data, struct_user_fpregs_struct_sz);
|
||||
PRE_READ((void *)data_arg, struct_user_fpregs_struct_sz);
|
||||
} else if (request == ptrace_setfpxregs) {
|
||||
PRE_READ((void *)data, struct_user_fpxregs_struct_sz);
|
||||
PRE_READ((void *)data_arg, struct_user_fpxregs_struct_sz);
|
||||
} else if (request == ptrace_setsiginfo) {
|
||||
PRE_READ((void *)data, siginfo_t_sz);
|
||||
PRE_READ((void *)data_arg, siginfo_t_sz);
|
||||
} else if (request == ptrace_setregset) {
|
||||
__sanitizer_iovec *iov = (__sanitizer_iovec *)data;
|
||||
__sanitizer_iovec *iov = (__sanitizer_iovec *)data_arg;
|
||||
PRE_READ(iov->iov_base, iov->iov_len);
|
||||
}
|
||||
}
|
||||
@@ -2552,25 +2554,26 @@ POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
|
||||
# if !SANITIZER_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \
|
||||
defined(__loongarch__) || SANITIZER_RISCV64)
|
||||
if (res >= 0 && data) {
|
||||
defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__))
|
||||
long data_arg = ptrace_data_arg(request, addr, data);
|
||||
if (res >= 0 && data_arg) {
|
||||
// Note that this is different from the interceptor in
|
||||
// sanitizer_common_interceptors.inc.
|
||||
// PEEK* requests return resulting values through data pointer.
|
||||
if (request == ptrace_getregs) {
|
||||
POST_WRITE((void *)data, struct_user_regs_struct_sz);
|
||||
POST_WRITE((void *)data_arg, struct_user_regs_struct_sz);
|
||||
} else if (request == ptrace_getfpregs) {
|
||||
POST_WRITE((void *)data, struct_user_fpregs_struct_sz);
|
||||
POST_WRITE((void *)data_arg, struct_user_fpregs_struct_sz);
|
||||
} else if (request == ptrace_getfpxregs) {
|
||||
POST_WRITE((void *)data, struct_user_fpxregs_struct_sz);
|
||||
POST_WRITE((void *)data_arg, struct_user_fpxregs_struct_sz);
|
||||
} else if (request == ptrace_getsiginfo) {
|
||||
POST_WRITE((void *)data, siginfo_t_sz);
|
||||
POST_WRITE((void *)data_arg, siginfo_t_sz);
|
||||
} else if (request == ptrace_getregset) {
|
||||
__sanitizer_iovec *iov = (__sanitizer_iovec *)data;
|
||||
__sanitizer_iovec *iov = (__sanitizer_iovec *)data_arg;
|
||||
POST_WRITE(iov->iov_base, iov->iov_len);
|
||||
} else if (request == ptrace_peekdata || request == ptrace_peektext ||
|
||||
request == ptrace_peekuser) {
|
||||
POST_WRITE((void *)data, sizeof(void *));
|
||||
POST_WRITE((void *)data_arg, sizeof(void *));
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
//===-- sanitizer_coverage_win_dll_thunk.cpp ------------------------------===//
|
||||
//
|
||||
// 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 defines a family of thunks that should be statically linked into
|
||||
// the DLLs that have instrumentation in order to delegate the calls to the
|
||||
// shared runtime that lives in the main binary.
|
||||
// See https://github.com/google/sanitizers/issues/209 for the details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef SANITIZER_DLL_THUNK
|
||||
#include "sanitizer_win_dll_thunk.h"
|
||||
// Sanitizer Coverage interface functions.
|
||||
#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
|
||||
#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
|
||||
#include "sanitizer_coverage_interface.inc"
|
||||
#endif // SANITIZER_DLL_THUNK
|
||||
@@ -1,26 +0,0 @@
|
||||
//===-- sanitizer_coverage_win_dynamic_runtime_thunk.cpp ------------------===//
|
||||
//
|
||||
// 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 defines things that need to be present in the application modules
|
||||
// to interact with Sanitizer Coverage, when it is included in a dll.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
|
||||
#define SANITIZER_IMPORT_INTERFACE 1
|
||||
#include "sanitizer_win_defs.h"
|
||||
// Define weak alias for all weak functions imported from sanitizer coverage.
|
||||
#define INTERFACE_FUNCTION(Name)
|
||||
#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
|
||||
#include "sanitizer_coverage_interface.inc"
|
||||
#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
|
||||
|
||||
namespace __sanitizer {
|
||||
// Add one, otherwise unused, external symbol to this object file so that the
|
||||
// Visual C++ linker includes it and reads the .drective section.
|
||||
void ForceWholeArchiveIncludeForSanCov() {}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
//===-- sanitizer_coverage_win_weak_interception.cpp ----------------------===//
|
||||
//
|
||||
// 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 module should be included in Sanitizer Coverage when it implemented as a
|
||||
// shared library on Windows (dll), in order to delegate the calls of weak
|
||||
// functions to the implementation in the main executable when a strong
|
||||
// definition is provided.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef SANITIZER_DYNAMIC
|
||||
#include "sanitizer_win_weak_interception.h"
|
||||
#include "sanitizer_interface_internal.h"
|
||||
#include "sancov_flags.h"
|
||||
// Check if strong definitions for weak functions are present in the main
|
||||
// executable. If that is the case, override dll functions to point to strong
|
||||
// implementations.
|
||||
#define INTERFACE_FUNCTION(Name)
|
||||
#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
|
||||
#include "sanitizer_coverage_interface.inc"
|
||||
#endif // SANITIZER_DYNAMIC
|
||||
@@ -120,7 +120,7 @@ class DeadlockDetectorTLS {
|
||||
u32 lock;
|
||||
u32 stk;
|
||||
};
|
||||
LockWithContext all_locks_with_contexts_[64];
|
||||
LockWithContext all_locks_with_contexts_[128];
|
||||
uptr n_all_locks_;
|
||||
};
|
||||
|
||||
|
||||
72
lib/tsan/sanitizer_common/sanitizer_dense_map.h
vendored
72
lib/tsan/sanitizer_common/sanitizer_dense_map.h
vendored
@@ -69,24 +69,14 @@ class DenseMapBase {
|
||||
setNumTombstones(0);
|
||||
}
|
||||
|
||||
/// Return 1 if the specified key is in the map, 0 otherwise.
|
||||
size_type count(const KeyT &Key) const {
|
||||
const BucketT *TheBucket;
|
||||
return LookupBucketFor(Key, TheBucket) ? 1 : 0;
|
||||
}
|
||||
/// Return true if the specified key is in the map, false otherwise.
|
||||
bool contains(const KeyT &Key) const { return doFind(Key) != nullptr; }
|
||||
|
||||
value_type *find(const KeyT &Key) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return TheBucket;
|
||||
return nullptr;
|
||||
}
|
||||
const value_type *find(const KeyT &Key) const {
|
||||
const BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return TheBucket;
|
||||
return nullptr;
|
||||
}
|
||||
/// Return 1 if the specified key is in the map, 0 otherwise.
|
||||
size_type count(const KeyT &Key) const { return contains(Key) ? 1 : 0; }
|
||||
|
||||
value_type *find(const KeyT &Key) { return doFind(Key); }
|
||||
const value_type *find(const KeyT &Key) const { return doFind(Key); }
|
||||
|
||||
/// Alternate version of find() which allows a different, and possibly
|
||||
/// less expensive, key type.
|
||||
@@ -95,25 +85,18 @@ class DenseMapBase {
|
||||
/// type used.
|
||||
template <class LookupKeyT>
|
||||
value_type *find_as(const LookupKeyT &Key) {
|
||||
BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return TheBucket;
|
||||
return nullptr;
|
||||
return doFind(Key);
|
||||
}
|
||||
template <class LookupKeyT>
|
||||
const value_type *find_as(const LookupKeyT &Key) const {
|
||||
const BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return TheBucket;
|
||||
return nullptr;
|
||||
return doFind(Key);
|
||||
}
|
||||
|
||||
/// lookup - Return the entry for the specified key, or a default
|
||||
/// constructed value if no such entry exists.
|
||||
ValueT lookup(const KeyT &Key) const {
|
||||
const BucketT *TheBucket;
|
||||
if (LookupBucketFor(Key, TheBucket))
|
||||
return TheBucket->getSecond();
|
||||
if (const BucketT *Bucket = doFind(Key))
|
||||
return Bucket->getSecond();
|
||||
return ValueT();
|
||||
}
|
||||
|
||||
@@ -184,8 +167,8 @@ class DenseMapBase {
|
||||
}
|
||||
|
||||
bool erase(const KeyT &Val) {
|
||||
BucketT *TheBucket;
|
||||
if (!LookupBucketFor(Val, TheBucket))
|
||||
BucketT *TheBucket = doFind(Val);
|
||||
if (!TheBucket)
|
||||
return false; // not in map.
|
||||
|
||||
TheBucket->getSecond().~ValueT();
|
||||
@@ -449,6 +432,35 @@ class DenseMapBase {
|
||||
return TheBucket;
|
||||
}
|
||||
|
||||
template <typename LookupKeyT>
|
||||
BucketT *doFind(const LookupKeyT &Val) {
|
||||
BucketT *BucketsPtr = getBuckets();
|
||||
const unsigned NumBuckets = getNumBuckets();
|
||||
if (NumBuckets == 0)
|
||||
return nullptr;
|
||||
|
||||
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())))
|
||||
return Bucket;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LookupKeyT>
|
||||
const BucketT *doFind(const LookupKeyT &Val) const {
|
||||
return const_cast<DenseMapBase *>(this)->doFind(Val);
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace __sanitizer {
|
||||
COMPILER_CHECK(errno_ENOMEM == ENOMEM);
|
||||
COMPILER_CHECK(errno_EBUSY == EBUSY);
|
||||
COMPILER_CHECK(errno_EINVAL == EINVAL);
|
||||
COMPILER_CHECK(errno_ERANGE == ERANGE);
|
||||
|
||||
// EOWNERDEAD is not present in some older platforms.
|
||||
#if defined(EOWNERDEAD)
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace __sanitizer {
|
||||
#define errno_ENOMEM 12
|
||||
#define errno_EBUSY 16
|
||||
#define errno_EINVAL 22
|
||||
#define errno_ERANGE 34
|
||||
#define errno_ENAMETOOLONG 36
|
||||
#define errno_ENOSYS 38
|
||||
|
||||
|
||||
@@ -94,7 +94,6 @@ void DisableCoreDumperIfNecessary() {}
|
||||
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
|
||||
void SetAlternateSignalStack() {}
|
||||
void UnsetAlternateSignalStack() {}
|
||||
void InitTlsSize() {}
|
||||
|
||||
bool SignalContext::IsStackOverflow() const { return false; }
|
||||
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
|
||||
@@ -445,6 +444,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
||||
return status == ZX_OK;
|
||||
}
|
||||
|
||||
bool TryMemCpy(void *dest, const void *src, uptr n) {
|
||||
// TODO: implement.
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME implement on this platform.
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
|
||||
|
||||
@@ -519,7 +523,6 @@ uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
|
||||
uptr MainThreadStackBase, MainThreadStackSize;
|
||||
|
||||
bool GetRandom(void *buffer, uptr length, bool blocking) {
|
||||
CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN);
|
||||
_zx_cprng_draw(buffer, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
21
lib/tsan/sanitizer_common/sanitizer_getauxval.h
vendored
21
lib/tsan/sanitizer_common/sanitizer_getauxval.h
vendored
@@ -21,22 +21,21 @@
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_FUCHSIA
|
||||
|
||||
# if (__GLIBC_PREREQ(2, 16) || (SANITIZER_ANDROID && __ANDROID_API__ >= 21) || \
|
||||
SANITIZER_FUCHSIA) && \
|
||||
!SANITIZER_GO
|
||||
# define SANITIZER_USE_GETAUXVAL 1
|
||||
# else
|
||||
# define SANITIZER_USE_GETAUXVAL 0
|
||||
# endif
|
||||
# if (__GLIBC_PREREQ(2, 16) || SANITIZER_ANDROID || SANITIZER_FUCHSIA) && \
|
||||
!SANITIZER_GO
|
||||
# define SANITIZER_USE_GETAUXVAL 1
|
||||
# else
|
||||
# define SANITIZER_USE_GETAUXVAL 0
|
||||
# endif
|
||||
|
||||
# if SANITIZER_USE_GETAUXVAL
|
||||
# include <sys/auxv.h>
|
||||
# else
|
||||
# if SANITIZER_USE_GETAUXVAL
|
||||
# include <sys/auxv.h>
|
||||
# else
|
||||
// The weak getauxval definition allows to check for the function at runtime.
|
||||
// This is useful for Android, when compiled at a lower API level yet running
|
||||
// on a more recent platform that offers the function.
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE unsigned long getauxval(unsigned long type);
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#elif SANITIZER_NETBSD
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@ __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_report_error_summary(const char *error_summary);
|
||||
|
||||
// Returns size of dynamically allocated block. This function can be overridden
|
||||
// by the client.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE __sanitizer::uptr
|
||||
__sanitizer_get_dtls_size(const void *tls_begin);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage(
|
||||
const __sanitizer::uptr *pcs, const __sanitizer::uptr len);
|
||||
@@ -71,6 +76,11 @@ 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
|
||||
|
||||
@@ -138,19 +138,25 @@
|
||||
// in a portable way by the language itself.
|
||||
namespace __sanitizer {
|
||||
|
||||
#if defined(_WIN64)
|
||||
#if defined(__UINTPTR_TYPE__)
|
||||
# if defined(__arm__) && defined(__linux__)
|
||||
// Linux Arm headers redefine __UINTPTR_TYPE__ and disagree with clang/gcc.
|
||||
typedef unsigned int uptr;
|
||||
typedef int sptr;
|
||||
# else
|
||||
typedef __UINTPTR_TYPE__ uptr;
|
||||
typedef __INTPTR_TYPE__ sptr;
|
||||
# endif
|
||||
#elif defined(_WIN64)
|
||||
// 64-bit Windows uses LLP64 data model.
|
||||
typedef unsigned long long uptr;
|
||||
typedef signed long long sptr;
|
||||
#else
|
||||
# if (SANITIZER_WORDSIZE == 64) || SANITIZER_APPLE || SANITIZER_WINDOWS
|
||||
typedef unsigned long uptr;
|
||||
typedef signed long sptr;
|
||||
# else
|
||||
#elif defined(_WIN32)
|
||||
typedef unsigned int uptr;
|
||||
typedef signed int sptr;
|
||||
# endif
|
||||
#endif // defined(_WIN64)
|
||||
#else
|
||||
# error Unsupported compiler, missing __UINTPTR_TYPE__
|
||||
#endif // defined(__UINTPTR_TYPE__)
|
||||
#if defined(__x86_64__)
|
||||
// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use
|
||||
// 64-bit pointer to unwind stack frame.
|
||||
@@ -194,16 +200,13 @@ typedef u64 OFF64_T;
|
||||
#ifdef __SIZE_TYPE__
|
||||
typedef __SIZE_TYPE__ usize;
|
||||
#else
|
||||
// Since we use this for operator new, usize must match the real size_t, but on
|
||||
// 32-bit Windows the definition of uptr does not actually match uintptr_t or
|
||||
// size_t because we are working around typedef mismatches for the (S)SIZE_T
|
||||
// types used in interception.h.
|
||||
// Until the definition of uptr has been fixed we have to special case Win32.
|
||||
# if SANITIZER_WINDOWS && SANITIZER_WORDSIZE == 32
|
||||
typedef unsigned int usize;
|
||||
# else
|
||||
typedef uptr usize;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__s390__) && !defined(__s390x__)
|
||||
typedef long ssize;
|
||||
#else
|
||||
typedef sptr ssize;
|
||||
#endif
|
||||
|
||||
typedef u64 tid_t;
|
||||
@@ -466,6 +469,9 @@ using namespace __sanitizer;
|
||||
namespace __msan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __nsan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __hwasan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
|
||||
lib->templ = internal_strdup(name_templ);
|
||||
lib->name = nullptr;
|
||||
lib->real_name = nullptr;
|
||||
lib->loaded = false;
|
||||
lib->range_id = kInvalidCodeRangeId;
|
||||
}
|
||||
|
||||
void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
@@ -43,7 +43,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
buf[0]) {
|
||||
for (uptr i = 0; i < count_; i++) {
|
||||
Lib *lib = &libs_[i];
|
||||
if (!lib->loaded && (!lib->real_name) &&
|
||||
if (!lib->loaded() && (!lib->real_name) &&
|
||||
TemplateMatch(lib->templ, name))
|
||||
lib->real_name = internal_strdup(buf.data());
|
||||
}
|
||||
@@ -70,28 +70,31 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
Die();
|
||||
}
|
||||
loaded = true;
|
||||
if (lib->loaded)
|
||||
if (lib->loaded())
|
||||
continue;
|
||||
VReport(1,
|
||||
"Matched called_from_lib suppression '%s' against library"
|
||||
" '%s'\n",
|
||||
lib->templ, mod.full_name());
|
||||
lib->loaded = true;
|
||||
lib->name = internal_strdup(mod.full_name());
|
||||
const uptr idx =
|
||||
atomic_load(&ignored_ranges_count_, memory_order_relaxed);
|
||||
CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
|
||||
ignored_code_ranges_[idx].begin = range.beg;
|
||||
ignored_code_ranges_[idx].end = range.end;
|
||||
ignored_code_ranges_[idx].OnLoad(range.beg, range.end);
|
||||
// Record the index of the ignored range.
|
||||
lib->range_id = idx;
|
||||
atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lib->loaded && !loaded) {
|
||||
Report("%s: library '%s' that was matched against called_from_lib"
|
||||
" suppression '%s' is unloaded\n",
|
||||
SanitizerToolName, lib->name, lib->templ);
|
||||
Die();
|
||||
if (lib->loaded() && !loaded) {
|
||||
VReport(1,
|
||||
"%s: library '%s' that was matched against called_from_lib"
|
||||
" suppression '%s' is unloaded\n",
|
||||
SanitizerToolName, lib->name, lib->templ);
|
||||
// The library is unloaded so mark the ignored code range as unloaded.
|
||||
ignored_code_ranges_[lib->range_id].OnUnload();
|
||||
lib->range_id = kInvalidCodeRangeId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +113,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
const uptr idx =
|
||||
atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
|
||||
CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
|
||||
instrumented_code_ranges_[idx].begin = range.beg;
|
||||
instrumented_code_ranges_[idx].end = range.end;
|
||||
instrumented_code_ranges_[idx].OnLoad(range.beg, range.end);
|
||||
atomic_store(&instrumented_ranges_count_, idx + 1,
|
||||
memory_order_release);
|
||||
}
|
||||
|
||||
35
lib/tsan/sanitizer_common/sanitizer_libignore.h
vendored
35
lib/tsan/sanitizer_common/sanitizer_libignore.h
vendored
@@ -49,26 +49,37 @@ class LibIgnore {
|
||||
bool IsPcInstrumented(uptr pc) const;
|
||||
|
||||
private:
|
||||
static const uptr kMaxIgnoredRanges = 128;
|
||||
static const uptr kMaxInstrumentedRanges = 1024;
|
||||
static const uptr kMaxLibs = 1024;
|
||||
static const uptr kInvalidCodeRangeId = -1;
|
||||
|
||||
struct Lib {
|
||||
char *templ;
|
||||
char *name;
|
||||
char *real_name; // target of symlink
|
||||
bool loaded;
|
||||
uptr range_id;
|
||||
bool loaded() const { return range_id != kInvalidCodeRangeId; };
|
||||
};
|
||||
|
||||
struct LibCodeRange {
|
||||
bool IsInRange(uptr pc) const {
|
||||
return (pc >= begin && pc < atomic_load(&end, memory_order_acquire));
|
||||
}
|
||||
|
||||
void OnLoad(uptr b, uptr e) {
|
||||
begin = b;
|
||||
atomic_store(&end, e, memory_order_release);
|
||||
}
|
||||
|
||||
void OnUnload() { atomic_store(&end, 0, memory_order_release); }
|
||||
|
||||
private:
|
||||
uptr begin;
|
||||
uptr end;
|
||||
// A value of 0 means the associated module was unloaded.
|
||||
atomic_uintptr_t end;
|
||||
};
|
||||
|
||||
inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
|
||||
return (pc >= range.begin && pc < range.end);
|
||||
}
|
||||
|
||||
static const uptr kMaxIgnoredRanges = 128;
|
||||
static const uptr kMaxInstrumentedRanges = 1024;
|
||||
static const uptr kMaxLibs = 1024;
|
||||
|
||||
// Hot part:
|
||||
atomic_uintptr_t ignored_ranges_count_;
|
||||
LibCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
|
||||
@@ -90,7 +101,7 @@ class LibIgnore {
|
||||
inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
|
||||
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, ignored_code_ranges_[i])) {
|
||||
if (ignored_code_ranges_[i].IsInRange(pc)) {
|
||||
*pc_in_ignored_lib = true;
|
||||
return true;
|
||||
}
|
||||
@@ -104,7 +115,7 @@ inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
|
||||
inline bool LibIgnore::IsPcInstrumented(uptr pc) const {
|
||||
const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, instrumented_code_ranges_[i]))
|
||||
if (instrumented_code_ranges_[i].IsInRange(pc))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
396
lib/tsan/sanitizer_common/sanitizer_linux.cpp
vendored
396
lib/tsan/sanitizer_common/sanitizer_linux.cpp
vendored
@@ -107,7 +107,9 @@ extern struct ps_strings *__ps_strings;
|
||||
# endif // SANITIZER_NETBSD
|
||||
|
||||
# if SANITIZER_SOLARIS
|
||||
# include <stddef.h>
|
||||
# include <stdlib.h>
|
||||
# include <sys/frame.h>
|
||||
# include <thread.h>
|
||||
# define environ _environ
|
||||
# endif
|
||||
@@ -132,9 +134,10 @@ const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
|
||||
// Are we using 32-bit or 64-bit Linux syscalls?
|
||||
// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
|
||||
// but it still needs to use 64-bit syscalls.
|
||||
# if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
|
||||
SANITIZER_WORDSIZE == 64 || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABIN32))
|
||||
# if SANITIZER_LINUX && \
|
||||
(defined(__x86_64__) || defined(__powerpc64__) || \
|
||||
SANITIZER_WORDSIZE == 64 || \
|
||||
(defined(__mips__) && defined(_ABIN32) && _MIPS_SIM == _ABIN32))
|
||||
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
|
||||
# else
|
||||
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
|
||||
@@ -152,6 +155,8 @@ const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
|
||||
|
||||
# if SANITIZER_FREEBSD
|
||||
# define SANITIZER_USE_GETENTROPY 1
|
||||
extern "C" void *__sys_mmap(void *addr, size_t len, int prot, int flags, int fd,
|
||||
off_t offset);
|
||||
# endif
|
||||
|
||||
namespace __sanitizer {
|
||||
@@ -160,33 +165,56 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {
|
||||
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset));
|
||||
}
|
||||
|
||||
# if SANITIZER_LINUX
|
||||
// Deletes the specified signal from newset, if it is not present in oldset
|
||||
// Equivalently: newset[signum] = newset[signum] & oldset[signum]
|
||||
static void KeepUnblocked(__sanitizer_sigset_t &newset,
|
||||
__sanitizer_sigset_t &oldset, int signum) {
|
||||
// FIXME: https://github.com/google/sanitizers/issues/1816
|
||||
if (SANITIZER_ANDROID || !internal_sigismember(&oldset, signum))
|
||||
internal_sigdelset(&newset, signum);
|
||||
}
|
||||
# endif
|
||||
|
||||
// Block asynchronous signals
|
||||
void BlockSignals(__sanitizer_sigset_t *oldset) {
|
||||
__sanitizer_sigset_t set;
|
||||
internal_sigfillset(&set);
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
__sanitizer_sigset_t newset;
|
||||
internal_sigfillset(&newset);
|
||||
|
||||
# if SANITIZER_LINUX
|
||||
__sanitizer_sigset_t currentset;
|
||||
|
||||
# if !SANITIZER_ANDROID
|
||||
// FIXME: https://github.com/google/sanitizers/issues/1816
|
||||
SetSigProcMask(NULL, ¤tset);
|
||||
|
||||
// Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
|
||||
// on any thread, setuid call hangs.
|
||||
// See test/sanitizer_common/TestCases/Linux/setuid.c.
|
||||
internal_sigdelset(&set, 33);
|
||||
# endif
|
||||
# if SANITIZER_LINUX
|
||||
KeepUnblocked(newset, currentset, 33);
|
||||
# endif // !SANITIZER_ANDROID
|
||||
|
||||
// Seccomp-BPF-sandboxed processes rely on SIGSYS to handle trapped syscalls.
|
||||
// If this signal is blocked, such calls cannot be handled and the process may
|
||||
// hang.
|
||||
internal_sigdelset(&set, 31);
|
||||
KeepUnblocked(newset, currentset, 31);
|
||||
|
||||
# if !SANITIZER_ANDROID
|
||||
// Don't block synchronous signals
|
||||
internal_sigdelset(&set, SIGSEGV);
|
||||
internal_sigdelset(&set, SIGBUS);
|
||||
internal_sigdelset(&set, SIGILL);
|
||||
internal_sigdelset(&set, SIGTRAP);
|
||||
internal_sigdelset(&set, SIGABRT);
|
||||
internal_sigdelset(&set, SIGFPE);
|
||||
internal_sigdelset(&set, SIGPIPE);
|
||||
# endif
|
||||
// but also don't unblock signals that the user had deliberately blocked.
|
||||
// FIXME: https://github.com/google/sanitizers/issues/1816
|
||||
KeepUnblocked(newset, currentset, SIGSEGV);
|
||||
KeepUnblocked(newset, currentset, SIGBUS);
|
||||
KeepUnblocked(newset, currentset, SIGILL);
|
||||
KeepUnblocked(newset, currentset, SIGTRAP);
|
||||
KeepUnblocked(newset, currentset, SIGABRT);
|
||||
KeepUnblocked(newset, currentset, SIGFPE);
|
||||
KeepUnblocked(newset, currentset, SIGPIPE);
|
||||
# endif //! SANITIZER_ANDROID
|
||||
|
||||
SetSigProcMask(&set, oldset);
|
||||
# endif // SANITIZER_LINUX
|
||||
|
||||
SetSigProcMask(&newset, oldset);
|
||||
}
|
||||
|
||||
ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) {
|
||||
@@ -218,7 +246,9 @@ ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); }
|
||||
# if !SANITIZER_S390
|
||||
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
|
||||
u64 offset) {
|
||||
# if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
|
||||
# if SANITIZER_FREEBSD
|
||||
return (uptr)__sys_mmap(addr, length, prot, flags, fd, offset);
|
||||
# elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
|
||||
return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
|
||||
offset);
|
||||
# else
|
||||
@@ -250,6 +280,11 @@ 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) {
|
||||
return internal_syscall(SYSCALL(close_range), lowfd, highfd, flags);
|
||||
}
|
||||
# endif
|
||||
uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); }
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
@@ -395,8 +430,9 @@ uptr internal_stat(const char *path, void *buf) {
|
||||
AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx);
|
||||
statx_to_stat(&bufx, (struct stat *)buf);
|
||||
return res;
|
||||
# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
|
||||
# elif ( \
|
||||
SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \
|
||||
(defined(__mips__) && defined(_ABIN32) && _MIPS_SIM == _ABIN32)) && \
|
||||
!SANITIZER_SPARC
|
||||
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
|
||||
0);
|
||||
@@ -433,8 +469,9 @@ uptr internal_lstat(const char *path, void *buf) {
|
||||
STATX_BASIC_STATS, (uptr)&bufx);
|
||||
statx_to_stat(&bufx, (struct stat *)buf);
|
||||
return res;
|
||||
# elif (defined(_LP64) || SANITIZER_X32 || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
|
||||
# elif ( \
|
||||
defined(_LP64) || SANITIZER_X32 || \
|
||||
(defined(__mips__) && defined(_ABIN32) && _MIPS_SIM == _ABIN32)) && \
|
||||
!SANITIZER_SPARC
|
||||
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
|
||||
AT_SYMLINK_NOFOLLOW);
|
||||
@@ -721,6 +758,11 @@ static void GetArgsAndEnv(char ***argv, char ***envp) {
|
||||
# if !SANITIZER_GO
|
||||
if (&__libc_stack_end) {
|
||||
uptr *stack_end = (uptr *)__libc_stack_end;
|
||||
// Linux/sparc64 needs an adjustment, cf. glibc
|
||||
// sysdeps/sparc/sparc{32,64}/dl-machine.h (DL_STACK_END).
|
||||
# if SANITIZER_LINUX && defined(__sparc__)
|
||||
stack_end = &stack_end[16];
|
||||
# endif
|
||||
// Normally argc can be obtained from *stack_end, however, on ARM glibc's
|
||||
// _start clobbers it:
|
||||
// https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/arm/start.S;hb=refs/heads/release/2.31/master#l75
|
||||
@@ -1014,34 +1056,29 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
|
||||
|
||||
# if !SANITIZER_NETBSD
|
||||
// ThreadLister implementation.
|
||||
ThreadLister::ThreadLister(pid_t pid) : pid_(pid), buffer_(4096) {
|
||||
char task_directory_path[80];
|
||||
internal_snprintf(task_directory_path, sizeof(task_directory_path),
|
||||
"/proc/%d/task/", pid);
|
||||
descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);
|
||||
if (internal_iserror(descriptor_)) {
|
||||
Report("Can't open /proc/%d/task for reading.\n", pid);
|
||||
}
|
||||
ThreadLister::ThreadLister(pid_t pid) : buffer_(4096) {
|
||||
task_path_.AppendF("/proc/%d/task", pid);
|
||||
}
|
||||
|
||||
ThreadLister::Result ThreadLister::ListThreads(
|
||||
InternalMmapVector<tid_t> *threads) {
|
||||
if (internal_iserror(descriptor_))
|
||||
int descriptor = internal_open(task_path_.data(), O_RDONLY | O_DIRECTORY);
|
||||
if (internal_iserror(descriptor)) {
|
||||
Report("Can't open %s for reading.\n", task_path_.data());
|
||||
return Error;
|
||||
internal_lseek(descriptor_, 0, SEEK_SET);
|
||||
}
|
||||
auto cleanup = at_scope_exit([&] { internal_close(descriptor); });
|
||||
threads->clear();
|
||||
|
||||
Result result = Ok;
|
||||
for (bool first_read = true;; first_read = false) {
|
||||
// Resize to max capacity if it was downsized by IsAlive.
|
||||
buffer_.resize(buffer_.capacity());
|
||||
CHECK_GE(buffer_.size(), 4096);
|
||||
uptr read = internal_getdents(
|
||||
descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size());
|
||||
descriptor, (struct linux_dirent *)buffer_.data(), buffer_.size());
|
||||
if (!read)
|
||||
return result;
|
||||
if (internal_iserror(read)) {
|
||||
Report("Can't read directory entries from /proc/%d/task.\n", pid_);
|
||||
Report("Can't read directory entries from %s.\n", task_path_.data());
|
||||
return Error;
|
||||
}
|
||||
|
||||
@@ -1079,26 +1116,33 @@ ThreadLister::Result ThreadLister::ListThreads(
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadLister::IsAlive(int tid) {
|
||||
const char *ThreadLister::LoadStatus(tid_t tid) {
|
||||
status_path_.clear();
|
||||
status_path_.AppendF("%s/%llu/status", task_path_.data(), tid);
|
||||
auto cleanup = at_scope_exit([&] {
|
||||
// Resize back to capacity if it is downsized by `ReadFileToVector`.
|
||||
buffer_.resize(buffer_.capacity());
|
||||
});
|
||||
if (!ReadFileToVector(status_path_.data(), &buffer_) || buffer_.empty())
|
||||
return nullptr;
|
||||
buffer_.push_back('\0');
|
||||
return buffer_.data();
|
||||
}
|
||||
|
||||
bool ThreadLister::IsAlive(tid_t tid) {
|
||||
// /proc/%d/task/%d/status uses same call to detect alive threads as
|
||||
// proc_task_readdir. See task_state implementation in Linux.
|
||||
char path[80];
|
||||
internal_snprintf(path, sizeof(path), "/proc/%d/task/%d/status", pid_, tid);
|
||||
if (!ReadFileToVector(path, &buffer_) || buffer_.empty())
|
||||
return false;
|
||||
buffer_.push_back(0);
|
||||
static const char kPrefix[] = "\nPPid:";
|
||||
const char *field = internal_strstr(buffer_.data(), kPrefix);
|
||||
const char *status = LoadStatus(tid);
|
||||
if (!status)
|
||||
return false;
|
||||
const char *field = internal_strstr(status, kPrefix);
|
||||
if (!field)
|
||||
return false;
|
||||
field += internal_strlen(kPrefix);
|
||||
return (int)internal_atoll(field) != 0;
|
||||
}
|
||||
|
||||
ThreadLister::~ThreadLister() {
|
||||
if (!internal_iserror(descriptor_))
|
||||
internal_close(descriptor_);
|
||||
}
|
||||
# endif
|
||||
|
||||
# if SANITIZER_WORDSIZE == 32
|
||||
@@ -1808,11 +1852,6 @@ int internal_uname(struct utsname *buf) {
|
||||
# endif
|
||||
|
||||
# if SANITIZER_ANDROID
|
||||
# if __ANDROID_API__ < 21
|
||||
extern "C" __attribute__((weak)) int dl_iterate_phdr(
|
||||
int (*)(struct dl_phdr_info *, size_t, void *), void *);
|
||||
# endif
|
||||
|
||||
static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
|
||||
void *data) {
|
||||
// Any name starting with "lib" indicates a bug in L where library base names
|
||||
@@ -1828,9 +1867,7 @@ static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
|
||||
static atomic_uint32_t android_api_level;
|
||||
|
||||
static AndroidApiLevel AndroidDetectApiLevelStatic() {
|
||||
# if __ANDROID_API__ <= 19
|
||||
return ANDROID_KITKAT;
|
||||
# elif __ANDROID_API__ <= 22
|
||||
# if __ANDROID_API__ <= 22
|
||||
return ANDROID_LOLLIPOP_MR1;
|
||||
# else
|
||||
return ANDROID_POST_LOLLIPOP;
|
||||
@@ -1838,8 +1875,6 @@ static AndroidApiLevel AndroidDetectApiLevelStatic() {
|
||||
}
|
||||
|
||||
static AndroidApiLevel AndroidDetectApiLevel() {
|
||||
if (!&dl_iterate_phdr)
|
||||
return ANDROID_KITKAT; // K or lower
|
||||
bool base_name_seen = false;
|
||||
dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
|
||||
if (base_name_seen)
|
||||
@@ -2014,6 +2049,18 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
|
||||
return Unknown;
|
||||
return esr & ESR_ELx_WNR ? Write : Read;
|
||||
# elif defined(__loongarch__)
|
||||
// In the musl environment, the Linux kernel uapi sigcontext.h is not
|
||||
// included in signal.h. To avoid missing the SC_ADDRERR_{RD,WR} macros,
|
||||
// copy them here. The LoongArch Linux kernel uapi is already stable,
|
||||
// so there's no need to worry about the value changing.
|
||||
# ifndef SC_ADDRERR_RD
|
||||
// Address error was due to memory load
|
||||
# define SC_ADDRERR_RD (1 << 30)
|
||||
# endif
|
||||
# ifndef SC_ADDRERR_WR
|
||||
// Address error was due to memory store
|
||||
# define SC_ADDRERR_WR (1 << 31)
|
||||
# endif
|
||||
u32 flags = ucontext->uc_mcontext.__flags;
|
||||
if (flags & SC_ADDRERR_RD)
|
||||
return SignalContext::Read;
|
||||
@@ -2154,8 +2201,26 @@ bool SignalContext::IsTrueFaultingAddress() const {
|
||||
UNUSED
|
||||
static const char *RegNumToRegName(int reg) {
|
||||
switch (reg) {
|
||||
# if SANITIZER_LINUX
|
||||
# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD
|
||||
# if defined(__x86_64__)
|
||||
# if SANITIZER_NETBSD
|
||||
# define REG_RAX _REG_RAX
|
||||
# define REG_RBX _REG_RBX
|
||||
# define REG_RCX _REG_RCX
|
||||
# define REG_RDX _REG_RDX
|
||||
# define REG_RDI _REG_RDI
|
||||
# define REG_RSI _REG_RSI
|
||||
# define REG_RBP _REG_RBP
|
||||
# define REG_RSP _REG_RSP
|
||||
# define REG_R8 _REG_R8
|
||||
# define REG_R9 _REG_R9
|
||||
# define REG_R10 _REG_R10
|
||||
# define REG_R11 _REG_R11
|
||||
# define REG_R12 _REG_R12
|
||||
# define REG_R13 _REG_R13
|
||||
# define REG_R14 _REG_R14
|
||||
# define REG_R15 _REG_R15
|
||||
# endif
|
||||
case REG_RAX:
|
||||
return "rax";
|
||||
case REG_RBX:
|
||||
@@ -2189,6 +2254,16 @@ static const char *RegNumToRegName(int reg) {
|
||||
case REG_R15:
|
||||
return "r15";
|
||||
# elif defined(__i386__)
|
||||
# if SANITIZER_NETBSD
|
||||
# define REG_EAX _REG_EAX
|
||||
# define REG_EBX _REG_EBX
|
||||
# define REG_ECX _REG_ECX
|
||||
# define REG_EDX _REG_EDX
|
||||
# define REG_EDI _REG_EDI
|
||||
# define REG_ESI _REG_ESI
|
||||
# define REG_EBP _REG_EBP
|
||||
# define REG_ESP _REG_ESP
|
||||
# endif
|
||||
case REG_EAX:
|
||||
return "eax";
|
||||
case REG_EBX:
|
||||
@@ -2205,32 +2280,170 @@ static const char *RegNumToRegName(int reg) {
|
||||
return "ebp";
|
||||
case REG_ESP:
|
||||
return "esp";
|
||||
# elif defined(__arm__)
|
||||
# ifdef MAKE_CASE
|
||||
# undef MAKE_CASE
|
||||
# endif
|
||||
# define REG_STR(reg) #reg
|
||||
# define MAKE_CASE(N) \
|
||||
case REG_R##N: \
|
||||
return REG_STR(r##N)
|
||||
MAKE_CASE(0);
|
||||
MAKE_CASE(1);
|
||||
MAKE_CASE(2);
|
||||
MAKE_CASE(3);
|
||||
MAKE_CASE(4);
|
||||
MAKE_CASE(5);
|
||||
MAKE_CASE(6);
|
||||
MAKE_CASE(7);
|
||||
MAKE_CASE(8);
|
||||
MAKE_CASE(9);
|
||||
MAKE_CASE(10);
|
||||
MAKE_CASE(11);
|
||||
MAKE_CASE(12);
|
||||
case REG_R13:
|
||||
return "sp";
|
||||
case REG_R14:
|
||||
return "lr";
|
||||
case REG_R15:
|
||||
return "pc";
|
||||
# elif defined(__aarch64__)
|
||||
# define REG_STR(reg) #reg
|
||||
# define MAKE_CASE(N) \
|
||||
case N: \
|
||||
return REG_STR(x##N)
|
||||
MAKE_CASE(0);
|
||||
MAKE_CASE(1);
|
||||
MAKE_CASE(2);
|
||||
MAKE_CASE(3);
|
||||
MAKE_CASE(4);
|
||||
MAKE_CASE(5);
|
||||
MAKE_CASE(6);
|
||||
MAKE_CASE(7);
|
||||
MAKE_CASE(8);
|
||||
MAKE_CASE(9);
|
||||
MAKE_CASE(10);
|
||||
MAKE_CASE(11);
|
||||
MAKE_CASE(12);
|
||||
MAKE_CASE(13);
|
||||
MAKE_CASE(14);
|
||||
MAKE_CASE(15);
|
||||
MAKE_CASE(16);
|
||||
MAKE_CASE(17);
|
||||
MAKE_CASE(18);
|
||||
MAKE_CASE(19);
|
||||
MAKE_CASE(20);
|
||||
MAKE_CASE(21);
|
||||
MAKE_CASE(22);
|
||||
MAKE_CASE(23);
|
||||
MAKE_CASE(24);
|
||||
MAKE_CASE(25);
|
||||
MAKE_CASE(26);
|
||||
MAKE_CASE(27);
|
||||
MAKE_CASE(28);
|
||||
case 29:
|
||||
return "fp";
|
||||
case 30:
|
||||
return "lr";
|
||||
case 31:
|
||||
return "sp";
|
||||
# endif
|
||||
# endif
|
||||
# endif // SANITIZER_LINUX && SANITIZER_GLIBC
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
# if SANITIZER_LINUX
|
||||
# if ((SANITIZER_LINUX && SANITIZER_GLIBC) || SANITIZER_NETBSD) && \
|
||||
(defined(__arm__) || defined(__aarch64__))
|
||||
static uptr GetArmRegister(ucontext_t *ctx, int RegNum) {
|
||||
switch (RegNum) {
|
||||
# if defined(__arm__) && !SANITIZER_NETBSD
|
||||
# ifdef MAKE_CASE
|
||||
# undef MAKE_CASE
|
||||
# endif
|
||||
# define MAKE_CASE(N) \
|
||||
case REG_R##N: \
|
||||
return ctx->uc_mcontext.arm_r##N
|
||||
MAKE_CASE(0);
|
||||
MAKE_CASE(1);
|
||||
MAKE_CASE(2);
|
||||
MAKE_CASE(3);
|
||||
MAKE_CASE(4);
|
||||
MAKE_CASE(5);
|
||||
MAKE_CASE(6);
|
||||
MAKE_CASE(7);
|
||||
MAKE_CASE(8);
|
||||
MAKE_CASE(9);
|
||||
MAKE_CASE(10);
|
||||
case REG_R11:
|
||||
return ctx->uc_mcontext.arm_fp;
|
||||
case REG_R12:
|
||||
return ctx->uc_mcontext.arm_ip;
|
||||
case REG_R13:
|
||||
return ctx->uc_mcontext.arm_sp;
|
||||
case REG_R14:
|
||||
return ctx->uc_mcontext.arm_lr;
|
||||
case REG_R15:
|
||||
return ctx->uc_mcontext.arm_pc;
|
||||
# elif defined(__aarch64__)
|
||||
# if SANITIZER_LINUX
|
||||
case 0 ... 30:
|
||||
return ctx->uc_mcontext.regs[RegNum];
|
||||
case 31:
|
||||
return ctx->uc_mcontext.sp;
|
||||
# elif SANITIZER_NETBSD
|
||||
case 0 ... 31:
|
||||
return ctx->uc_mcontext.__gregs[RegNum];
|
||||
# endif
|
||||
# endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
# endif // SANITIZER_LINUX && SANITIZER_GLIBC && (defined(__arm__) ||
|
||||
// defined(__aarch64__))
|
||||
|
||||
UNUSED
|
||||
static void DumpSingleReg(ucontext_t *ctx, int RegNum) {
|
||||
const char *RegName = RegNumToRegName(RegNum);
|
||||
# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD
|
||||
# if defined(__x86_64__)
|
||||
Printf("%s%s = 0x%016llx ", internal_strlen(RegName) == 2 ? " " : "",
|
||||
RegName, ctx->uc_mcontext.gregs[RegNum]);
|
||||
RegName,
|
||||
# if SANITIZER_LINUX
|
||||
ctx->uc_mcontext.gregs[RegNum]
|
||||
# elif SANITIZER_NETBSD
|
||||
ctx->uc_mcontext.__gregs[RegNum]
|
||||
# endif
|
||||
);
|
||||
# elif defined(__i386__)
|
||||
Printf("%s = 0x%08x ", RegName, ctx->uc_mcontext.gregs[RegNum]);
|
||||
Printf("%s = 0x%08x ", RegName,
|
||||
# if SANITIZER_LINUX
|
||||
ctx->uc_mcontext.gregs[RegNum]
|
||||
# elif SANITIZER_NETBSD
|
||||
ctx->uc_mcontext.__gregs[RegNum]
|
||||
# endif
|
||||
);
|
||||
# elif defined(__arm__)
|
||||
Printf("%s%s = 0x%08zx ", internal_strlen(RegName) == 2 ? " " : "", RegName,
|
||||
GetArmRegister(ctx, RegNum));
|
||||
# elif defined(__aarch64__)
|
||||
Printf("%s%s = 0x%016zx ", internal_strlen(RegName) == 2 ? " " : "", RegName,
|
||||
GetArmRegister(ctx, RegNum));
|
||||
# else
|
||||
(void)RegName;
|
||||
# endif
|
||||
}
|
||||
# else
|
||||
(void)RegName;
|
||||
# endif
|
||||
}
|
||||
|
||||
void SignalContext::DumpAllRegisters(void *context) {
|
||||
ucontext_t *ucontext = (ucontext_t *)context;
|
||||
# if SANITIZER_LINUX
|
||||
# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD
|
||||
# if defined(__x86_64__)
|
||||
Report("Register values:\n");
|
||||
DumpSingleReg(ucontext, REG_RAX);
|
||||
@@ -2269,6 +2482,35 @@ void SignalContext::DumpAllRegisters(void *context) {
|
||||
DumpSingleReg(ucontext, REG_EBP);
|
||||
DumpSingleReg(ucontext, REG_ESP);
|
||||
Printf("\n");
|
||||
# elif defined(__arm__) && !SANITIZER_NETBSD
|
||||
Report("Register values:\n");
|
||||
DumpSingleReg(ucontext, REG_R0);
|
||||
DumpSingleReg(ucontext, REG_R1);
|
||||
DumpSingleReg(ucontext, REG_R2);
|
||||
DumpSingleReg(ucontext, REG_R3);
|
||||
Printf("\n");
|
||||
DumpSingleReg(ucontext, REG_R4);
|
||||
DumpSingleReg(ucontext, REG_R5);
|
||||
DumpSingleReg(ucontext, REG_R6);
|
||||
DumpSingleReg(ucontext, REG_R7);
|
||||
Printf("\n");
|
||||
DumpSingleReg(ucontext, REG_R8);
|
||||
DumpSingleReg(ucontext, REG_R9);
|
||||
DumpSingleReg(ucontext, REG_R10);
|
||||
DumpSingleReg(ucontext, REG_R11);
|
||||
Printf("\n");
|
||||
DumpSingleReg(ucontext, REG_R12);
|
||||
DumpSingleReg(ucontext, REG_R13);
|
||||
DumpSingleReg(ucontext, REG_R14);
|
||||
DumpSingleReg(ucontext, REG_R15);
|
||||
Printf("\n");
|
||||
# elif defined(__aarch64__)
|
||||
Report("Register values:\n");
|
||||
for (int i = 0; i <= 31; ++i) {
|
||||
DumpSingleReg(ucontext, i);
|
||||
if (i % 4 == 3)
|
||||
Printf("\n");
|
||||
}
|
||||
# else
|
||||
(void)ucontext;
|
||||
# endif
|
||||
@@ -2310,6 +2552,8 @@ void SignalContext::DumpAllRegisters(void *context) {
|
||||
# else
|
||||
(void)ucontext;
|
||||
# endif
|
||||
# else
|
||||
(void)ucontext;
|
||||
# endif
|
||||
// FIXME: Implement this for other OSes and architectures.
|
||||
}
|
||||
@@ -2404,7 +2648,19 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
# if SANITIZER_SOLARIS
|
||||
ucontext_t *ucontext = (ucontext_t *)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_PC];
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS;
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_SP] + STACK_BIAS;
|
||||
// Avoid SEGV when dereferencing sp on stack overflow with non-faulting load.
|
||||
// This requires a SPARC V9 CPU. Cannot use #ASI_PNF here: only supported
|
||||
// since clang-19.
|
||||
# if defined(__sparcv9)
|
||||
asm("ldxa [%[fp]] 0x82, %[bp]"
|
||||
# else
|
||||
asm("lduwa [%[fp]] 0x82, %[bp]"
|
||||
# endif
|
||||
: [bp] "=r"(*bp)
|
||||
: [fp] "r"(&((struct frame *)*sp)->fr_savfp));
|
||||
if (*bp)
|
||||
*bp += STACK_BIAS;
|
||||
# else
|
||||
// Historical BSDism here.
|
||||
struct sigcontext *scontext = (struct sigcontext *)context;
|
||||
@@ -2415,8 +2671,8 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
*pc = scontext->si_regs.pc;
|
||||
*sp = scontext->si_regs.u_regs[14];
|
||||
# endif
|
||||
# endif
|
||||
*bp = (uptr)((uhwptr *)*sp)[14] + STACK_BIAS;
|
||||
# endif
|
||||
# elif defined(__mips__)
|
||||
ucontext_t *ucontext = (ucontext_t *)context;
|
||||
*pc = ucontext->uc_mcontext.pc;
|
||||
@@ -2459,9 +2715,7 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
|
||||
void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
|
||||
|
||||
void InitializePlatformEarly() {
|
||||
// Do nothing.
|
||||
}
|
||||
void InitializePlatformEarly() { InitTlsSize(); }
|
||||
|
||||
void CheckASLR() {
|
||||
# if SANITIZER_NETBSD
|
||||
|
||||
8
lib/tsan/sanitizer_common/sanitizer_linux.h
vendored
8
lib/tsan/sanitizer_common/sanitizer_linux.h
vendored
@@ -97,19 +97,19 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
|
||||
class ThreadLister {
|
||||
public:
|
||||
explicit ThreadLister(pid_t pid);
|
||||
~ThreadLister();
|
||||
enum Result {
|
||||
Error,
|
||||
Incomplete,
|
||||
Ok,
|
||||
};
|
||||
Result ListThreads(InternalMmapVector<tid_t> *threads);
|
||||
const char *LoadStatus(tid_t tid);
|
||||
|
||||
private:
|
||||
bool IsAlive(int tid);
|
||||
bool IsAlive(tid_t tid);
|
||||
|
||||
pid_t pid_;
|
||||
int descriptor_ = -1;
|
||||
InternalScopedString task_path_;
|
||||
InternalScopedString status_path_;
|
||||
InternalMmapVector<char> buffer_;
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
# include <sys/resource.h>
|
||||
# include <syslog.h>
|
||||
|
||||
# if SANITIZER_GLIBC
|
||||
# include <gnu/libc-version.h>
|
||||
# endif
|
||||
|
||||
# if !defined(ElfW)
|
||||
# define ElfW(type) Elf_##type
|
||||
# endif
|
||||
@@ -53,7 +57,7 @@
|
||||
// that, it was never implemented. So just define it to zero.
|
||||
# undef MAP_NORESERVE
|
||||
# define MAP_NORESERVE 0
|
||||
extern const Elf_Auxinfo *__elf_aux_vector;
|
||||
extern const Elf_Auxinfo *__elf_aux_vector __attribute__((weak));
|
||||
extern "C" int __sys_sigaction(int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
# endif
|
||||
@@ -196,27 +200,6 @@ bool SetEnv(const char *name, const char *value) {
|
||||
}
|
||||
# endif
|
||||
|
||||
__attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
|
||||
int *patch) {
|
||||
# ifdef _CS_GNU_LIBC_VERSION
|
||||
char buf[64];
|
||||
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
|
||||
if (len >= sizeof(buf))
|
||||
return false;
|
||||
buf[len] = 0;
|
||||
static const char kGLibC[] = "glibc ";
|
||||
if (internal_strncmp(buf, kGLibC, sizeof(kGLibC) - 1) != 0)
|
||||
return false;
|
||||
const char *p = buf + sizeof(kGLibC) - 1;
|
||||
*major = internal_simple_strtoll(p, &p, 10);
|
||||
*minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
|
||||
*patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
|
||||
return true;
|
||||
# else
|
||||
return false;
|
||||
# endif
|
||||
}
|
||||
|
||||
// True if we can use dlpi_tls_data. glibc before 2.25 may leave NULL (BZ
|
||||
// #19826) so dlpi_tls_data cannot be used.
|
||||
//
|
||||
@@ -226,112 +209,166 @@ __attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
|
||||
__attribute__((unused)) static int g_use_dlpi_tls_data;
|
||||
|
||||
# if SANITIZER_GLIBC && !SANITIZER_GO
|
||||
__attribute__((unused)) static size_t g_tls_size;
|
||||
void InitTlsSize() {
|
||||
int major, minor, patch;
|
||||
g_use_dlpi_tls_data =
|
||||
GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25;
|
||||
|
||||
# if defined(__aarch64__) || defined(__x86_64__) || \
|
||||
defined(__powerpc64__) || defined(__loongarch__)
|
||||
void *get_tls_static_info = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
|
||||
size_t tls_align;
|
||||
((void (*)(size_t *, size_t *))get_tls_static_info)(&g_tls_size, &tls_align);
|
||||
# endif
|
||||
static void GetGLibcVersion(int *major, int *minor, int *patch) {
|
||||
const char *p = gnu_get_libc_version();
|
||||
*major = internal_simple_strtoll(p, &p, 10);
|
||||
// Caller does not expect anything else.
|
||||
CHECK_EQ(*major, 2);
|
||||
*minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
|
||||
*patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
|
||||
}
|
||||
# else
|
||||
void InitTlsSize() {}
|
||||
# endif // SANITIZER_GLIBC && !SANITIZER_GO
|
||||
|
||||
// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage
|
||||
// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan
|
||||
// to get the pointer to thread-specific data keys in the thread control block.
|
||||
# if (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS) && \
|
||||
!SANITIZER_ANDROID && !SANITIZER_GO
|
||||
// sizeof(struct pthread) from glibc.
|
||||
static atomic_uintptr_t thread_descriptor_size;
|
||||
|
||||
static uptr ThreadDescriptorSizeFallback() {
|
||||
uptr val = 0;
|
||||
# if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
|
||||
# if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \
|
||||
SANITIZER_RISCV64
|
||||
int major;
|
||||
int minor;
|
||||
int patch;
|
||||
if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
|
||||
/* sizeof(struct pthread) values from various glibc versions. */
|
||||
if (SANITIZER_X32)
|
||||
val = 1728; // Assume only one particular version for x32.
|
||||
// For ARM sizeof(struct pthread) changed in Glibc 2.23.
|
||||
else if (SANITIZER_ARM)
|
||||
val = minor <= 22 ? 1120 : 1216;
|
||||
else if (minor <= 3)
|
||||
val = FIRST_32_SECOND_64(1104, 1696);
|
||||
else if (minor == 4)
|
||||
val = FIRST_32_SECOND_64(1120, 1728);
|
||||
else if (minor == 5)
|
||||
val = FIRST_32_SECOND_64(1136, 1728);
|
||||
else if (minor <= 9)
|
||||
val = FIRST_32_SECOND_64(1136, 1712);
|
||||
else if (minor == 10)
|
||||
val = FIRST_32_SECOND_64(1168, 1776);
|
||||
else if (minor == 11 || (minor == 12 && patch == 1))
|
||||
val = FIRST_32_SECOND_64(1168, 2288);
|
||||
else if (minor <= 14)
|
||||
val = FIRST_32_SECOND_64(1168, 2304);
|
||||
else if (minor < 32) // Unknown version
|
||||
val = FIRST_32_SECOND_64(1216, 2304);
|
||||
else // minor == 32
|
||||
val = FIRST_32_SECOND_64(1344, 2496);
|
||||
}
|
||||
# elif defined(__s390__) || defined(__sparc__)
|
||||
GetGLibcVersion(&major, &minor, &patch);
|
||||
# endif
|
||||
|
||||
# if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
|
||||
/* sizeof(struct pthread) values from various glibc versions. */
|
||||
if (SANITIZER_X32)
|
||||
return 1728; // Assume only one particular version for x32.
|
||||
// For ARM sizeof(struct pthread) changed in Glibc 2.23.
|
||||
if (SANITIZER_ARM)
|
||||
return minor <= 22 ? 1120 : 1216;
|
||||
if (minor <= 3)
|
||||
return FIRST_32_SECOND_64(1104, 1696);
|
||||
if (minor == 4)
|
||||
return FIRST_32_SECOND_64(1120, 1728);
|
||||
if (minor == 5)
|
||||
return FIRST_32_SECOND_64(1136, 1728);
|
||||
if (minor <= 9)
|
||||
return FIRST_32_SECOND_64(1136, 1712);
|
||||
if (minor == 10)
|
||||
return FIRST_32_SECOND_64(1168, 1776);
|
||||
if (minor == 11 || (minor == 12 && patch == 1))
|
||||
return FIRST_32_SECOND_64(1168, 2288);
|
||||
if (minor <= 14)
|
||||
return FIRST_32_SECOND_64(1168, 2304);
|
||||
if (minor < 32) // Unknown version
|
||||
return FIRST_32_SECOND_64(1216, 2304);
|
||||
// minor == 32
|
||||
return FIRST_32_SECOND_64(1344, 2496);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_RISCV64
|
||||
// TODO: consider adding an optional runtime check for an unknown (untested)
|
||||
// glibc version
|
||||
if (minor <= 28) // WARNING: the highest tested version is 2.29
|
||||
return 1772; // no guarantees for this one
|
||||
if (minor <= 31)
|
||||
return 1772; // tested against glibc 2.29, 2.31
|
||||
return 1936; // tested against glibc 2.32
|
||||
# endif
|
||||
|
||||
# if defined(__s390__) || defined(__sparc__)
|
||||
// The size of a prefix of TCB including pthread::{specific_1stblock,specific}
|
||||
// suffices. Just return offsetof(struct pthread, specific_used), which hasn't
|
||||
// changed since 2007-05. Technically this applies to i386/x86_64 as well but
|
||||
// we call _dl_get_tls_static_info and need the precise size of struct
|
||||
// pthread.
|
||||
return FIRST_32_SECOND_64(524, 1552);
|
||||
# elif defined(__mips__)
|
||||
# endif
|
||||
|
||||
# if defined(__mips__)
|
||||
// TODO(sagarthakur): add more values as per different glibc versions.
|
||||
val = FIRST_32_SECOND_64(1152, 1776);
|
||||
# elif SANITIZER_LOONGARCH64
|
||||
val = 1856; // from glibc 2.36
|
||||
# elif SANITIZER_RISCV64
|
||||
int major;
|
||||
int minor;
|
||||
int patch;
|
||||
if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
|
||||
// TODO: consider adding an optional runtime check for an unknown (untested)
|
||||
// glibc version
|
||||
if (minor <= 28) // WARNING: the highest tested version is 2.29
|
||||
val = 1772; // no guarantees for this one
|
||||
else if (minor <= 31)
|
||||
val = 1772; // tested against glibc 2.29, 2.31
|
||||
else
|
||||
val = 1936; // tested against glibc 2.32
|
||||
return FIRST_32_SECOND_64(1152, 1776);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LOONGARCH64
|
||||
return 1856; // from glibc 2.36
|
||||
# endif
|
||||
|
||||
# if defined(__aarch64__)
|
||||
// The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
|
||||
return 1776;
|
||||
# endif
|
||||
|
||||
# if defined(__powerpc64__)
|
||||
return 1776; // from glibc.ppc64le 2.20-8.fc21
|
||||
# endif
|
||||
}
|
||||
# endif // SANITIZER_GLIBC && !SANITIZER_GO
|
||||
|
||||
# if SANITIZER_FREEBSD && !SANITIZER_GO
|
||||
// FIXME: Implementation is very GLIBC specific, but it's used by FreeBSD.
|
||||
static uptr ThreadDescriptorSizeFallback() {
|
||||
# if defined(__s390__) || defined(__sparc__)
|
||||
// The size of a prefix of TCB including pthread::{specific_1stblock,specific}
|
||||
// suffices. Just return offsetof(struct pthread, specific_used), which hasn't
|
||||
// changed since 2007-05. Technically this applies to i386/x86_64 as well but
|
||||
// we call _dl_get_tls_static_info and need the precise size of struct
|
||||
// pthread.
|
||||
return FIRST_32_SECOND_64(524, 1552);
|
||||
# endif
|
||||
|
||||
# if defined(__mips__)
|
||||
// TODO(sagarthakur): add more values as per different glibc versions.
|
||||
return FIRST_32_SECOND_64(1152, 1776);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LOONGARCH64
|
||||
return 1856; // from glibc 2.36
|
||||
# endif
|
||||
|
||||
# if defined(__aarch64__)
|
||||
// The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
|
||||
return 1776;
|
||||
# endif
|
||||
|
||||
# if defined(__powerpc64__)
|
||||
return 1776; // from glibc.ppc64le 2.20-8.fc21
|
||||
# endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
# endif // SANITIZER_FREEBSD && !SANITIZER_GO
|
||||
|
||||
# if (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO
|
||||
// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage
|
||||
// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan
|
||||
// to get the pointer to thread-specific data keys in the thread control block.
|
||||
// sizeof(struct pthread) from glibc.
|
||||
static uptr thread_descriptor_size;
|
||||
|
||||
uptr ThreadDescriptorSize() { return thread_descriptor_size; }
|
||||
|
||||
# if SANITIZER_GLIBC
|
||||
__attribute__((unused)) static size_t g_tls_size;
|
||||
# endif
|
||||
|
||||
void InitTlsSize() {
|
||||
# if SANITIZER_GLIBC
|
||||
int major, minor, patch;
|
||||
GetGLibcVersion(&major, &minor, &patch);
|
||||
g_use_dlpi_tls_data = major == 2 && minor >= 25;
|
||||
|
||||
if (major == 2 && minor >= 34) {
|
||||
// _thread_db_sizeof_pthread is a GLIBC_PRIVATE symbol that is exported in
|
||||
// glibc 2.34 and later.
|
||||
if (unsigned *psizeof = static_cast<unsigned *>(
|
||||
dlsym(RTLD_DEFAULT, "_thread_db_sizeof_pthread"))) {
|
||||
thread_descriptor_size = *psizeof;
|
||||
}
|
||||
}
|
||||
|
||||
# elif defined(__aarch64__)
|
||||
// The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
|
||||
val = 1776;
|
||||
# elif defined(__powerpc64__)
|
||||
val = 1776; // from glibc.ppc64le 2.20-8.fc21
|
||||
# endif
|
||||
return val;
|
||||
}
|
||||
# if defined(__aarch64__) || defined(__x86_64__) || \
|
||||
defined(__powerpc64__) || defined(__loongarch__)
|
||||
auto *get_tls_static_info = (void (*)(size_t *, size_t *))dlsym(
|
||||
RTLD_DEFAULT, "_dl_get_tls_static_info");
|
||||
size_t tls_align;
|
||||
// Can be null if static link.
|
||||
if (get_tls_static_info)
|
||||
get_tls_static_info(&g_tls_size, &tls_align);
|
||||
# endif
|
||||
|
||||
uptr ThreadDescriptorSize() {
|
||||
uptr val = atomic_load_relaxed(&thread_descriptor_size);
|
||||
if (val)
|
||||
return val;
|
||||
// _thread_db_sizeof_pthread is a GLIBC_PRIVATE symbol that is exported in
|
||||
// glibc 2.34 and later.
|
||||
if (unsigned *psizeof = static_cast<unsigned *>(
|
||||
dlsym(RTLD_DEFAULT, "_thread_db_sizeof_pthread")))
|
||||
val = *psizeof;
|
||||
if (!val)
|
||||
val = ThreadDescriptorSizeFallback();
|
||||
atomic_store_relaxed(&thread_descriptor_size, val);
|
||||
return val;
|
||||
# endif // SANITIZER_GLIBC
|
||||
|
||||
if (!thread_descriptor_size)
|
||||
thread_descriptor_size = ThreadDescriptorSizeFallback();
|
||||
}
|
||||
|
||||
# if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 || \
|
||||
@@ -354,7 +391,13 @@ static uptr TlsPreTcbSize() {
|
||||
return kTlsPreTcbSize;
|
||||
}
|
||||
# endif
|
||||
# else // (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO
|
||||
void InitTlsSize() {}
|
||||
uptr ThreadDescriptorSize() { return 0; }
|
||||
# endif // (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO
|
||||
|
||||
# if (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS) && \
|
||||
!SANITIZER_ANDROID && !SANITIZER_GO
|
||||
namespace {
|
||||
struct TlsBlock {
|
||||
uptr begin, end, align;
|
||||
@@ -626,25 +669,32 @@ uptr GetTlsSize() {
|
||||
}
|
||||
# endif
|
||||
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
uptr *tls_addr, uptr *tls_size) {
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
|
||||
uptr *tls_begin, uptr *tls_end) {
|
||||
# if SANITIZER_GO
|
||||
// Stub implementation for Go.
|
||||
*stk_addr = *stk_size = *tls_addr = *tls_size = 0;
|
||||
*stk_begin = 0;
|
||||
*stk_end = 0;
|
||||
*tls_begin = 0;
|
||||
*tls_end = 0;
|
||||
# else
|
||||
GetTls(tls_addr, tls_size);
|
||||
uptr tls_addr = 0;
|
||||
uptr tls_size = 0;
|
||||
GetTls(&tls_addr, &tls_size);
|
||||
*tls_begin = tls_addr;
|
||||
*tls_end = tls_addr + tls_size;
|
||||
|
||||
uptr stack_top, stack_bottom;
|
||||
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
|
||||
*stk_addr = stack_bottom;
|
||||
*stk_size = stack_top - stack_bottom;
|
||||
*stk_begin = stack_bottom;
|
||||
*stk_end = stack_top;
|
||||
|
||||
if (!main) {
|
||||
// If stack and tls intersect, make them non-intersecting.
|
||||
if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
|
||||
if (*stk_addr + *stk_size < *tls_addr + *tls_size)
|
||||
*tls_size = *stk_addr + *stk_size - *tls_addr;
|
||||
*stk_size = *tls_addr - *stk_addr;
|
||||
if (*tls_begin > *stk_begin && *tls_begin < *stk_end) {
|
||||
if (*stk_end < *tls_end)
|
||||
*tls_end = *stk_end;
|
||||
*stk_end = *tls_begin;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
@@ -723,11 +773,6 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
# if SANITIZER_ANDROID && __ANDROID_API__ < 21
|
||||
extern "C" __attribute__((weak)) int dl_iterate_phdr(
|
||||
int (*)(struct dl_phdr_info *, size_t, void *), void *);
|
||||
# endif
|
||||
|
||||
static bool requiresProcmaps() {
|
||||
# if SANITIZER_ANDROID && __ANDROID_API__ <= 22
|
||||
// Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
|
||||
@@ -890,11 +935,8 @@ extern "C" SANITIZER_WEAK_ATTRIBUTE int __android_log_write(int prio,
|
||||
void WriteOneLineToSyslog(const char *s) {
|
||||
if (&async_safe_write_log) {
|
||||
async_safe_write_log(SANITIZER_ANDROID_LOG_INFO, GetProcessName(), s);
|
||||
} else if (AndroidGetApiLevel() > ANDROID_KITKAT) {
|
||||
syslog(LOG_INFO, "%s", s);
|
||||
} else {
|
||||
CHECK(&__android_log_write);
|
||||
__android_log_write(SANITIZER_ANDROID_LOG_INFO, nullptr, s);
|
||||
syslog(LOG_INFO, "%s", s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
64
lib/tsan/sanitizer_common/sanitizer_mac.cpp
vendored
64
lib/tsan/sanitizer_common/sanitizer_mac.cpp
vendored
@@ -45,7 +45,7 @@ extern char **environ;
|
||||
# define SANITIZER_OS_TRACE 0
|
||||
# endif
|
||||
|
||||
// import new crash reporting api
|
||||
// Integrate with CrashReporter library if available
|
||||
# if defined(__has_include) && __has_include(<CrashReporterClient.h>)
|
||||
# define HAVE_CRASHREPORTERCLIENT_H 1
|
||||
# include <CrashReporterClient.h>
|
||||
@@ -545,9 +545,6 @@ uptr GetTlsSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitTlsSize() {
|
||||
}
|
||||
|
||||
uptr TlsBaseAddr() {
|
||||
uptr segbase = 0;
|
||||
#if defined(__x86_64__)
|
||||
@@ -572,21 +569,18 @@ uptr TlsSize() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
uptr *tls_addr, uptr *tls_size) {
|
||||
#if !SANITIZER_GO
|
||||
uptr stack_top, stack_bottom;
|
||||
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
|
||||
*stk_addr = stack_bottom;
|
||||
*stk_size = stack_top - stack_bottom;
|
||||
*tls_addr = TlsBaseAddr();
|
||||
*tls_size = TlsSize();
|
||||
#else
|
||||
*stk_addr = 0;
|
||||
*stk_size = 0;
|
||||
*tls_addr = 0;
|
||||
*tls_size = 0;
|
||||
#endif
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
|
||||
uptr *tls_begin, uptr *tls_end) {
|
||||
# if !SANITIZER_GO
|
||||
GetThreadStackTopAndBottom(main, stk_end, stk_begin);
|
||||
*tls_begin = TlsBaseAddr();
|
||||
*tls_end = *tls_begin + TlsSize();
|
||||
# else
|
||||
*stk_begin = 0;
|
||||
*stk_end = 0;
|
||||
*tls_begin = 0;
|
||||
*tls_end = 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
void ListOfModules::init() {
|
||||
@@ -788,7 +782,11 @@ void WriteOneLineToSyslog(const char *s) {
|
||||
if (GetMacosAlignedVersion() >= MacosVersion(10, 12)) {
|
||||
os_log_error(OS_LOG_DEFAULT, "%{public}s", s);
|
||||
} else {
|
||||
#pragma clang diagnostic push
|
||||
// as_log is deprecated.
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -798,8 +796,13 @@ static char crashreporter_info_buff[__sanitizer::kErrorMessageBufferSize] = {};
|
||||
static Mutex crashreporter_info_mutex;
|
||||
|
||||
extern "C" {
|
||||
// Integrate with crash reporter libraries.
|
||||
|
||||
#if HAVE_CRASHREPORTERCLIENT_H
|
||||
// Available in CRASHREPORTER_ANNOTATIONS_VERSION 5+
|
||||
# ifdef CRASHREPORTER_ANNOTATIONS_INITIALIZER
|
||||
CRASHREPORTER_ANNOTATIONS_INITIALIZER()
|
||||
# else
|
||||
// Support for older CrashRerporter annotiations
|
||||
CRASH_REPORTER_CLIENT_HIDDEN
|
||||
struct crashreporter_annotations_t gCRAnnotations
|
||||
__attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) = {
|
||||
@@ -810,17 +813,17 @@ struct crashreporter_annotations_t gCRAnnotations
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
#if CRASHREPORTER_ANNOTATIONS_VERSION > 4
|
||||
# if CRASHREPORTER_ANNOTATIONS_VERSION > 4
|
||||
0,
|
||||
#endif
|
||||
# endif
|
||||
};
|
||||
|
||||
#else
|
||||
// fall back to old crashreporter api
|
||||
# endif
|
||||
# else
|
||||
// Revert to previous crash reporter API if client header is not available
|
||||
static const char *__crashreporter_info__ __attribute__((__used__)) =
|
||||
&crashreporter_info_buff[0];
|
||||
asm(".desc ___crashreporter_info__, 0x10");
|
||||
#endif
|
||||
#endif // HAVE_CRASHREPORTERCLIENT_H
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -843,6 +846,9 @@ void LogFullErrorReport(const char *buffer) {
|
||||
#if !SANITIZER_GO
|
||||
// Log with os_trace. This will make it into the crash log.
|
||||
#if SANITIZER_OS_TRACE
|
||||
#pragma clang diagnostic push
|
||||
// os_trace is deprecated.
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
if (GetMacosAlignedVersion() >= MacosVersion(10, 10)) {
|
||||
// os_trace requires the message (format parameter) to be a string literal.
|
||||
if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
|
||||
@@ -860,6 +866,7 @@ void LogFullErrorReport(const char *buffer) {
|
||||
if (common_flags()->log_to_syslog)
|
||||
os_trace("Consult syslog for more information.");
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
// Log to syslog.
|
||||
@@ -970,8 +977,9 @@ static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
|
||||
LowLevelAllocator allocator_for_env;
|
||||
|
||||
static bool ShouldCheckInterceptors() {
|
||||
// Restrict "interceptors working?" check to ASan and TSan.
|
||||
const char *sanitizer_names[] = {"AddressSanitizer", "ThreadSanitizer"};
|
||||
// Restrict "interceptors working?" check
|
||||
const char *sanitizer_names[] = {"AddressSanitizer", "ThreadSanitizer",
|
||||
"RealtimeSanitizer"};
|
||||
size_t count = sizeof(sanitizer_names) / sizeof(sanitizer_names[0]);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (internal_strcmp(sanitizer_names[i], SanitizerToolName) == 0)
|
||||
|
||||
@@ -84,6 +84,25 @@
|
||||
#define SI_NOT_MAC 1
|
||||
#endif
|
||||
|
||||
#if SANITIZER_APPLE
|
||||
# include <Availability.h>
|
||||
|
||||
// aligned_alloc was introduced in OSX 10.15
|
||||
// Linking will fail when using an older SDK
|
||||
# if defined(__MAC_10_15)
|
||||
// macOS 10.15 is greater than our minimal deployment target. To ensure we
|
||||
// generate a weak reference so the dylib continues to work on older
|
||||
// systems, we need to forward declare the intercepted function as "weak
|
||||
// imports".
|
||||
SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
|
||||
__sanitizer::usize __size);
|
||||
# define SI_MAC_SDK_10_15_AVAILABLE 1
|
||||
# else
|
||||
# define SI_MAC_SDK_10_15_AVAILABLE 0
|
||||
# endif // defined(__MAC_10_15)
|
||||
|
||||
#endif // SANITIZER_APPLE
|
||||
|
||||
#if SANITIZER_IOS
|
||||
#define SI_IOS 1
|
||||
#else
|
||||
@@ -183,9 +202,16 @@
|
||||
#define SANITIZER_INTERCEPT_FPUTS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PUTS SI_POSIX
|
||||
|
||||
#define SANITIZER_INTERCEPT_CREAT64 (SI_GLIBC || SI_SOLARIS32)
|
||||
#define SANITIZER_INTERCEPT_FCNTL64 (SI_GLIBC || SI_SOLARIS32)
|
||||
#define SANITIZER_INTERCEPT_OPEN64 (SI_GLIBC || SI_SOLARIS32)
|
||||
#define SANITIZER_INTERCEPT_OPENAT64 (SI_GLIBC || SI_SOLARIS32)
|
||||
|
||||
#define SANITIZER_INTERCEPT_PREAD64 (SI_GLIBC || SI_SOLARIS32)
|
||||
#define SANITIZER_INTERCEPT_PWRITE64 (SI_GLIBC || SI_SOLARIS32)
|
||||
|
||||
#define SANITIZER_INTERCEPT_LSEEK64 (SI_GLIBC || SI_SOLARIS32)
|
||||
|
||||
#define SANITIZER_INTERCEPT_READV SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_WRITEV SI_POSIX
|
||||
|
||||
@@ -232,8 +258,12 @@
|
||||
(SI_FREEBSD || SI_NETBSD || SI_LINUX || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CLOCK_GETCPUCLOCKID \
|
||||
(SI_LINUX || SI_FREEBSD || SI_NETBSD)
|
||||
// TODO: This should be SI_POSIX, adding glibc first until I have time
|
||||
// to verify all timer_t typedefs on other platforms.
|
||||
#define SANITIZER_INTERCEPT_TIMER_CREATE SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_GETITIMER SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_TIME SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_TIMESPEC_GET SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_GLOB (SI_GLIBC || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_GLOB64 SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT___B64_TO SI_LINUX_NOT_ANDROID
|
||||
@@ -274,8 +304,9 @@
|
||||
#if SI_LINUX_NOT_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64)
|
||||
#define SANITIZER_INTERCEPT_PTRACE 1
|
||||
defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
|
||||
defined(__sparc__))
|
||||
# define SANITIZER_INTERCEPT_PTRACE 1
|
||||
#else
|
||||
#define SANITIZER_INTERCEPT_PTRACE 0
|
||||
#endif
|
||||
@@ -314,6 +345,8 @@
|
||||
#define SANITIZER_INTERCEPT_GETGROUPS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_POLL SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_EPOLL (SI_LINUX)
|
||||
#define SANITIZER_INTERCEPT_KQUEUE (SI_FREEBSD || SI_NETBSD || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_WORDEXP \
|
||||
(SI_FREEBSD || SI_NETBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
@@ -494,7 +527,8 @@
|
||||
#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_ALIGNED_ALLOC (!SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_ALIGNED_ALLOC \
|
||||
(!SI_MAC || SI_MAC_SDK_10_15_AVAILABLE)
|
||||
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_WCSLEN 1
|
||||
@@ -559,10 +593,8 @@
|
||||
#define SANITIZER_INTERCEPT_SHA1 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_MD4 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_RMD160 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_MD5 (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_FSEEK (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_FSEEK SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MD2 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_SHA2 (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_CDB SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_VIS (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_POPEN SI_POSIX
|
||||
@@ -601,7 +633,13 @@
|
||||
// FIXME: also available from musl 1.2.5
|
||||
#define SANITIZER_INTERCEPT_PREADV2 (SI_LINUX && __GLIBC_PREREQ(2, 26))
|
||||
#define SANITIZER_INTERCEPT_PWRITEV2 (SI_LINUX && __GLIBC_PREREQ(2, 26))
|
||||
|
||||
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
|
||||
__MAC_OS_X_VERSION_MIN_REQUIRED >= 130000
|
||||
# define SI_MAC_OS_DEPLOYMENT_MIN_13_00 1
|
||||
#else
|
||||
# define SI_MAC_OS_DEPLOYMENT_MIN_13_00 0
|
||||
#endif
|
||||
#define SANITIZER_INTERCEPT_FREADLINK (SI_MAC && SI_MAC_OS_DEPLOYMENT_MIN_13_00)
|
||||
// This macro gives a way for downstream users to override the above
|
||||
// interceptor macros irrespective of the platform they are on. They have
|
||||
// to do two things:
|
||||
|
||||
@@ -547,6 +547,7 @@ unsigned pid_t_sz = sizeof(pid_t);
|
||||
unsigned timeval_sz = sizeof(timeval);
|
||||
unsigned uid_t_sz = sizeof(uid_t);
|
||||
unsigned gid_t_sz = sizeof(gid_t);
|
||||
unsigned fpos_t_sz = sizeof(fpos_t);
|
||||
unsigned mbstate_t_sz = sizeof(mbstate_t);
|
||||
unsigned sigset_t_sz = sizeof(sigset_t);
|
||||
unsigned struct_timezone_sz = sizeof(struct timezone);
|
||||
@@ -2487,8 +2488,6 @@ const unsigned RMD160_return_length = RMD160_DIGEST_STRING_LENGTH;
|
||||
const unsigned MD5_CTX_sz = sizeof(MD5_CTX);
|
||||
const unsigned MD5_return_length = MD5_DIGEST_STRING_LENGTH;
|
||||
|
||||
const unsigned fpos_t_sz = sizeof(fpos_t);
|
||||
|
||||
const unsigned MD2_CTX_sz = sizeof(MD2_CTX);
|
||||
const unsigned MD2_return_length = MD2_DIGEST_STRING_LENGTH;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ extern unsigned pid_t_sz;
|
||||
extern unsigned timeval_sz;
|
||||
extern unsigned uid_t_sz;
|
||||
extern unsigned gid_t_sz;
|
||||
extern unsigned fpos_t_sz;
|
||||
extern unsigned mbstate_t_sz;
|
||||
extern unsigned struct_timezone_sz;
|
||||
extern unsigned struct_tms_sz;
|
||||
@@ -2335,8 +2336,6 @@ extern const unsigned RMD160_return_length;
|
||||
extern const unsigned MD5_CTX_sz;
|
||||
extern const unsigned MD5_return_length;
|
||||
|
||||
extern const unsigned fpos_t_sz;
|
||||
|
||||
extern const unsigned MD2_CTX_sz;
|
||||
extern const unsigned MD2_return_length;
|
||||
|
||||
|
||||
@@ -94,8 +94,9 @@
|
||||
#if SANITIZER_LINUX
|
||||
# include <utime.h>
|
||||
# include <sys/ptrace.h>
|
||||
# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__hexagon__) || defined(__loongarch__) ||SANITIZER_RISCV64
|
||||
# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__hexagon__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
|
||||
defined(__sparc__)
|
||||
# include <asm/ptrace.h>
|
||||
# ifdef __arm__
|
||||
typedef struct user_fpregs elf_fpregset_t;
|
||||
@@ -117,15 +118,16 @@ typedef struct user_fpregs elf_fpregset_t;
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_GLIBC
|
||||
#include <fstab.h>
|
||||
#include <net/if_ppp.h>
|
||||
#include <netax25/ax25.h>
|
||||
#include <netipx/ipx.h>
|
||||
#include <netrom/netrom.h>
|
||||
#include <obstack.h>
|
||||
#if HAVE_RPC_XDR_H
|
||||
# include <rpc/xdr.h>
|
||||
#endif
|
||||
#include <scsi/scsi.h>
|
||||
# include <linux/filter.h>
|
||||
# include <net/if_ppp.h>
|
||||
# include <netax25/ax25.h>
|
||||
# include <netipx/ipx.h>
|
||||
# include <netrom/netrom.h>
|
||||
# include <obstack.h>
|
||||
# if HAVE_RPC_XDR_H
|
||||
# include <rpc/xdr.h>
|
||||
# endif
|
||||
# include <scsi/scsi.h>
|
||||
#else
|
||||
#include <linux/if_ppp.h>
|
||||
#include <linux/kd.h>
|
||||
@@ -358,11 +360,12 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
const int wordexp_wrde_dooffs = WRDE_DOOFFS;
|
||||
# endif // !SANITIZER_ANDROID
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__s390__) || defined(__loongarch__)|| SANITIZER_RISCV64)
|
||||
#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
|
||||
defined(__sparc__))
|
||||
# if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
|
||||
unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
|
||||
unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
|
||||
#elif SANITIZER_RISCV64
|
||||
@@ -377,19 +380,22 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
#elif defined(__s390__)
|
||||
unsigned struct_user_regs_struct_sz = sizeof(struct _user_regs_struct);
|
||||
unsigned struct_user_fpregs_struct_sz = sizeof(struct _user_fpregs_struct);
|
||||
#else
|
||||
# elif defined(__sparc__)
|
||||
unsigned struct_user_regs_struct_sz = sizeof(struct sunos_regs);
|
||||
unsigned struct_user_fpregs_struct_sz = sizeof(struct sunos_fp);
|
||||
# else
|
||||
unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
|
||||
unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
|
||||
#endif // __mips64 || __powerpc64__ || __aarch64__ || __loongarch__
|
||||
#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
|
||||
defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \
|
||||
defined(__loongarch__) || SANITIZER_RISCV64
|
||||
# endif // __mips64 || __powerpc64__ || __aarch64__ || __loongarch__
|
||||
# if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
|
||||
defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \
|
||||
defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__)
|
||||
unsigned struct_user_fpxregs_struct_sz = 0;
|
||||
#else
|
||||
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
|
||||
#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
|
||||
// || __s390__ || __loongarch__
|
||||
#ifdef __arm__
|
||||
// || __s390__ || __loongarch__ || SANITIZER_RISCV64 || __sparc__
|
||||
# ifdef __arm__
|
||||
unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
|
||||
#else
|
||||
unsigned struct_user_vfpregs_struct_sz = 0;
|
||||
@@ -531,13 +537,16 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
|
||||
unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
|
||||
unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
|
||||
#endif // SANITIZER_GLIBC
|
||||
unsigned struct_sock_fprog_sz = sizeof(struct sock_fprog);
|
||||
# endif // SANITIZER_GLIBC
|
||||
|
||||
#if !SANITIZER_ANDROID && !SANITIZER_APPLE
|
||||
# if !SANITIZER_ANDROID && !SANITIZER_APPLE
|
||||
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
|
||||
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
||||
#endif
|
||||
|
||||
unsigned fpos_t_sz = sizeof(fpos_t);
|
||||
|
||||
const unsigned long __sanitizer_bufsiz = BUFSIZ;
|
||||
|
||||
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||
@@ -1084,7 +1093,7 @@ CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
|
||||
|
||||
#if SANITIZER_LINUX && (__ANDROID_API__ >= 21 || __GLIBC_PREREQ (2, 14))
|
||||
# if SANITIZER_LINUX && (SANITIZER_ANDROID || __GLIBC_PREREQ(2, 14))
|
||||
CHECK_TYPE_SIZE(mmsghdr);
|
||||
CHECK_SIZE_AND_OFFSET(mmsghdr, msg_hdr);
|
||||
CHECK_SIZE_AND_OFFSET(mmsghdr, msg_len);
|
||||
|
||||
@@ -98,10 +98,13 @@ const unsigned struct_kernel_stat64_sz = 104;
|
||||
const unsigned struct_kernel_stat_sz = 144;
|
||||
const unsigned struct_kernel_stat64_sz = 104;
|
||||
#elif defined(__mips__)
|
||||
const unsigned struct_kernel_stat_sz =
|
||||
SANITIZER_ANDROID
|
||||
? FIRST_32_SECOND_64(104, 128)
|
||||
: FIRST_32_SECOND_64((_MIPS_SIM == _ABIN32) ? 176 : 160, 216);
|
||||
const unsigned struct_kernel_stat_sz = SANITIZER_ANDROID
|
||||
? FIRST_32_SECOND_64(104, 128)
|
||||
# if defined(_ABIN32) && _MIPS_SIM == _ABIN32
|
||||
: FIRST_32_SECOND_64(176, 216);
|
||||
# else
|
||||
: FIRST_32_SECOND_64(160, 216);
|
||||
# endif
|
||||
const unsigned struct_kernel_stat64_sz = 104;
|
||||
#elif defined(__s390__) && !defined(__s390x__)
|
||||
const unsigned struct_kernel_stat_sz = 64;
|
||||
@@ -313,7 +316,7 @@ extern unsigned struct_statvfs_sz;
|
||||
|
||||
struct __sanitizer_iovec {
|
||||
void *iov_base;
|
||||
uptr iov_len;
|
||||
usize iov_len;
|
||||
};
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
@@ -389,6 +392,16 @@ typedef long __sanitizer_time_t;
|
||||
|
||||
typedef long __sanitizer_suseconds_t;
|
||||
|
||||
struct __sanitizer_timespec {
|
||||
__sanitizer_time_t tv_sec; /* seconds */
|
||||
u64 tv_nsec; /* nanoseconds */
|
||||
};
|
||||
|
||||
struct __sanitizer_itimerspec {
|
||||
struct __sanitizer_timespec it_interval; /* timer period */
|
||||
struct __sanitizer_timespec it_value; /* timer expiration */
|
||||
};
|
||||
|
||||
struct __sanitizer_timeval {
|
||||
__sanitizer_time_t tv_sec;
|
||||
__sanitizer_suseconds_t tv_usec;
|
||||
@@ -513,6 +526,7 @@ struct __sanitizer_dirent64 {
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
extern unsigned struct_sock_fprog_sz;
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) && !defined(_LP64)
|
||||
@@ -590,7 +604,7 @@ struct __sanitizer_siginfo_pad {
|
||||
#if SANITIZER_LINUX
|
||||
# define SANITIZER_HAS_SIGINFO 1
|
||||
union __sanitizer_siginfo {
|
||||
struct {
|
||||
__extension__ struct {
|
||||
int si_signo;
|
||||
# if SANITIZER_MIPS
|
||||
int si_code;
|
||||
@@ -855,10 +869,11 @@ typedef void __sanitizer_FILE;
|
||||
# define SANITIZER_HAS_STRUCT_FILE 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64)
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
|
||||
defined(__sparc__))
|
||||
extern unsigned struct_user_regs_struct_sz;
|
||||
extern unsigned struct_user_fpregs_struct_sz;
|
||||
extern unsigned struct_user_fpxregs_struct_sz;
|
||||
@@ -880,9 +895,24 @@ extern int ptrace_setsiginfo;
|
||||
extern int ptrace_getregset;
|
||||
extern int ptrace_setregset;
|
||||
extern int ptrace_geteventmsg;
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
// Helper for the ptrace interceptor.
|
||||
template <class T>
|
||||
inline T ptrace_data_arg(int request, T addr, T data) {
|
||||
# if SANITIZER_LINUX && SANITIZER_SPARC
|
||||
// As described in ptrace(2), the meanings of addr and data are reversed
|
||||
// for the PTRACE_GETREGS, PTRACE_GETFPREGS, PTRACE_GETREGS, and
|
||||
// PTRACE_GETFPREGS requests on Linux/sparc64.
|
||||
if (request == ptrace_getregs || request == ptrace_getfpregs ||
|
||||
request == ptrace_setregs || request == ptrace_setfpregs)
|
||||
return addr;
|
||||
else
|
||||
# endif
|
||||
return data;
|
||||
}
|
||||
# endif
|
||||
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
extern unsigned struct_shminfo_sz;
|
||||
extern unsigned struct_shm_info_sz;
|
||||
extern int shmctl_ipc_stat;
|
||||
@@ -1050,7 +1080,7 @@ extern unsigned struct_serial_struct_sz;
|
||||
extern unsigned struct_sockaddr_ax25_sz;
|
||||
extern unsigned struct_unimapdesc_sz;
|
||||
extern unsigned struct_unimapinit_sz;
|
||||
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
# endif // SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
|
||||
extern const unsigned long __sanitizer_bufsiz;
|
||||
|
||||
@@ -1064,6 +1094,8 @@ extern unsigned struct_sioc_sg_req_sz;
|
||||
extern unsigned struct_sioc_vif_req_sz;
|
||||
#endif
|
||||
|
||||
extern unsigned fpos_t_sz;
|
||||
|
||||
// ioctl request identifiers
|
||||
|
||||
// A special value to mark ioctls that are not present on the target platform,
|
||||
@@ -1500,6 +1532,10 @@ extern const int si_SEGV_ACCERR;
|
||||
|
||||
#define SIGACTION_SYMNAME sigaction
|
||||
|
||||
# if SANITIZER_LINUX
|
||||
typedef void *__sanitizer_timer_t;
|
||||
# endif
|
||||
|
||||
#endif // SANITIZER_LINUX || SANITIZER_APPLE
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ethernet.h>
|
||||
#include <sys/filio.h>
|
||||
#include <sys/ipc.h>
|
||||
@@ -135,6 +136,8 @@ namespace __sanitizer {
|
||||
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
|
||||
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
||||
|
||||
unsigned fpos_t_sz = sizeof(fpos_t);
|
||||
|
||||
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||
|
||||
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
||||
|
||||
@@ -418,6 +418,8 @@ extern unsigned struct_winsize_sz;
|
||||
extern unsigned struct_sioc_sg_req_sz;
|
||||
extern unsigned struct_sioc_vif_req_sz;
|
||||
|
||||
extern unsigned fpos_t_sz;
|
||||
|
||||
// ioctl request identifiers
|
||||
|
||||
// A special value to mark ioctls that are not present on the target platform,
|
||||
|
||||
10
lib/tsan/sanitizer_common/sanitizer_posix.cpp
vendored
10
lib/tsan/sanitizer_common/sanitizer_posix.cpp
vendored
@@ -353,7 +353,15 @@ bool ShouldMockFailureToOpen(const char *path) {
|
||||
internal_strncmp(path, "/proc/", 6) == 0;
|
||||
}
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
|
||||
bool OpenReadsVaArgs(int oflag) {
|
||||
# ifdef O_TMPFILE
|
||||
return (oflag & (O_CREAT | O_TMPFILE)) != 0;
|
||||
# else
|
||||
return (oflag & O_CREAT) != 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
|
||||
int GetNamedMappingFd(const char *name, uptr size, int *flags) {
|
||||
if (!common_flags()->decorate_proc_maps || !name)
|
||||
return -1;
|
||||
|
||||
6
lib/tsan/sanitizer_common/sanitizer_posix.h
vendored
6
lib/tsan/sanitizer_common/sanitizer_posix.h
vendored
@@ -28,6 +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
|
||||
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);
|
||||
@@ -86,7 +89,7 @@ int internal_pthread_join(void *th, void **ret);
|
||||
return REAL(pthread_create)(th, attr, callback, param); \
|
||||
} \
|
||||
int internal_pthread_join(void *th, void **ret) { \
|
||||
return REAL(pthread_join(th, ret)); \
|
||||
return REAL(pthread_join)(th, ret); \
|
||||
} \
|
||||
} // namespace __sanitizer
|
||||
|
||||
@@ -108,6 +111,7 @@ bool IsStateDetached(int state);
|
||||
fd_t ReserveStandardFds(fd_t fd);
|
||||
|
||||
bool ShouldMockFailureToOpen(const char *path);
|
||||
bool OpenReadsVaArgs(int oflag);
|
||||
|
||||
// Create a non-file mapping with a given /proc/self/maps name.
|
||||
uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name);
|
||||
|
||||
@@ -288,26 +288,86 @@ bool SignalContext::IsStackOverflow() const {
|
||||
|
||||
#endif // SANITIZER_GO
|
||||
|
||||
static void SetNonBlock(int fd) {
|
||||
int res = fcntl(fd, F_GETFL, 0);
|
||||
CHECK(!internal_iserror(res, nullptr));
|
||||
|
||||
res |= O_NONBLOCK;
|
||||
res = fcntl(fd, F_SETFL, res);
|
||||
CHECK(!internal_iserror(res, nullptr));
|
||||
}
|
||||
|
||||
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
||||
uptr page_size = GetPageSizeCached();
|
||||
// Checking too large memory ranges is slow.
|
||||
CHECK_LT(size, page_size * 10);
|
||||
int sock_pair[2];
|
||||
if (pipe(sock_pair))
|
||||
return false;
|
||||
uptr bytes_written =
|
||||
internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
|
||||
int write_errno;
|
||||
bool result;
|
||||
if (internal_iserror(bytes_written, &write_errno)) {
|
||||
CHECK_EQ(EFAULT, write_errno);
|
||||
result = false;
|
||||
} else {
|
||||
result = (bytes_written == size);
|
||||
while (size) {
|
||||
// `read` from `fds[0]` into a dummy buffer to free up the pipe buffer for
|
||||
// more `write` is slower than just recreating a pipe.
|
||||
int fds[2];
|
||||
CHECK_EQ(0, pipe(fds));
|
||||
|
||||
auto cleanup = at_scope_exit([&]() {
|
||||
internal_close(fds[0]);
|
||||
internal_close(fds[1]);
|
||||
});
|
||||
|
||||
SetNonBlock(fds[1]);
|
||||
|
||||
int write_errno;
|
||||
uptr w = internal_write(fds[1], reinterpret_cast<char *>(beg), size);
|
||||
if (internal_iserror(w, &write_errno)) {
|
||||
if (write_errno == EINTR)
|
||||
continue;
|
||||
CHECK_EQ(EFAULT, write_errno);
|
||||
return false;
|
||||
}
|
||||
size -= w;
|
||||
beg += w;
|
||||
}
|
||||
internal_close(sock_pair[0]);
|
||||
internal_close(sock_pair[1]);
|
||||
return result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryMemCpy(void *dest, const void *src, uptr n) {
|
||||
if (!n)
|
||||
return true;
|
||||
int fds[2];
|
||||
CHECK_EQ(0, pipe(fds));
|
||||
|
||||
auto cleanup = at_scope_exit([&]() {
|
||||
internal_close(fds[0]);
|
||||
internal_close(fds[1]);
|
||||
});
|
||||
|
||||
SetNonBlock(fds[0]);
|
||||
SetNonBlock(fds[1]);
|
||||
|
||||
char *d = static_cast<char *>(dest);
|
||||
const char *s = static_cast<const char *>(src);
|
||||
|
||||
while (n) {
|
||||
int e;
|
||||
uptr w = internal_write(fds[1], s, n);
|
||||
if (internal_iserror(w, &e)) {
|
||||
if (e == EINTR)
|
||||
continue;
|
||||
CHECK_EQ(EFAULT, e);
|
||||
return false;
|
||||
}
|
||||
s += w;
|
||||
n -= w;
|
||||
|
||||
while (w) {
|
||||
uptr r = internal_read(fds[0], d, w);
|
||||
if (internal_iserror(r, &e)) {
|
||||
CHECK_EQ(EINTR, e);
|
||||
continue;
|
||||
}
|
||||
|
||||
d += r;
|
||||
w -= r;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformPrepareForSandboxing(void *args) {
|
||||
@@ -483,7 +543,11 @@ 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
|
||||
|
||||
internal_execve(program, const_cast<char **>(&argv[0]),
|
||||
const_cast<char *const *>(envp));
|
||||
|
||||
@@ -433,7 +433,9 @@ void MemoryMappingLayout::DumpListOfModules(
|
||||
MemoryMappedSegmentData data;
|
||||
segment.data_ = &data;
|
||||
while (Next(&segment)) {
|
||||
if (segment.filename[0] == '\0') continue;
|
||||
// skip the __PAGEZERO segment, its vmsize is 0
|
||||
if (segment.filename[0] == '\0' || (segment.start == segment.end))
|
||||
continue;
|
||||
LoadedModule *cur_module = nullptr;
|
||||
if (!modules->empty() &&
|
||||
0 == internal_strcmp(segment.filename, modules->back().full_name())) {
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment.
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
// Avoid conflict between `_TIME_BITS` defined vs. `_FILE_OFFSET_BITS`
|
||||
// undefined in some Linux configurations.
|
||||
#undef _TIME_BITS
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_SOLARIS
|
||||
# include <fcntl.h>
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
// The asm hack only works with GCC and Clang.
|
||||
# if !defined(_WIN32)
|
||||
|
||||
asm("memcpy = __sanitizer_internal_memcpy");
|
||||
asm("memmove = __sanitizer_internal_memmove");
|
||||
asm("memset = __sanitizer_internal_memset");
|
||||
asm(R"(
|
||||
.set memcpy, __sanitizer_internal_memcpy
|
||||
.set memmove, __sanitizer_internal_memmove
|
||||
.set memset, __sanitizer_internal_memset
|
||||
)");
|
||||
|
||||
# if defined(__cplusplus) && \
|
||||
!defined(SANITIZER_COMMON_REDEFINE_BUILTINS_IN_STD)
|
||||
|
||||
@@ -137,10 +137,6 @@ class ThreadSuspender {
|
||||
};
|
||||
|
||||
bool ThreadSuspender::SuspendThread(tid_t tid) {
|
||||
// Are we already attached to this thread?
|
||||
// Currently this check takes linear time, however the number of threads is
|
||||
// usually small.
|
||||
if (suspended_threads_list_.ContainsTid(tid)) return false;
|
||||
int pterrno;
|
||||
if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
|
||||
&pterrno)) {
|
||||
@@ -217,17 +213,28 @@ bool ThreadSuspender::SuspendAllThreads() {
|
||||
switch (thread_lister.ListThreads(&threads)) {
|
||||
case ThreadLister::Error:
|
||||
ResumeAllThreads();
|
||||
VReport(1, "Failed to list threads\n");
|
||||
return false;
|
||||
case ThreadLister::Incomplete:
|
||||
VReport(1, "Incomplete list\n");
|
||||
retry = true;
|
||||
break;
|
||||
case ThreadLister::Ok:
|
||||
break;
|
||||
}
|
||||
for (tid_t tid : threads) {
|
||||
// Are we already attached to this thread?
|
||||
// Currently this check takes linear time, however the number of threads
|
||||
// is usually small.
|
||||
if (suspended_threads_list_.ContainsTid(tid))
|
||||
continue;
|
||||
if (SuspendThread(tid))
|
||||
retry = true;
|
||||
else
|
||||
VReport(2, "%llu/status: %s\n", tid, thread_lister.LoadStatus(tid));
|
||||
}
|
||||
if (retry)
|
||||
VReport(1, "SuspendAllThreads retry: %d\n", i);
|
||||
}
|
||||
return suspended_threads_list_.ThreadCount();
|
||||
}
|
||||
|
||||
@@ -185,17 +185,17 @@ class Symbolizer final {
|
||||
class ModuleNameOwner {
|
||||
public:
|
||||
explicit ModuleNameOwner(Mutex *synchronized_by)
|
||||
: last_match_(nullptr), mu_(synchronized_by) {
|
||||
: mu_(synchronized_by), last_match_(nullptr) {
|
||||
storage_.reserve(kInitialCapacity);
|
||||
}
|
||||
const char *GetOwnedCopy(const char *str);
|
||||
|
||||
private:
|
||||
static const uptr kInitialCapacity = 1000;
|
||||
InternalMmapVector<const char*> storage_;
|
||||
const char *last_match_;
|
||||
|
||||
Mutex *mu_;
|
||||
const char *last_match_ SANITIZER_GUARDED_BY(mu_);
|
||||
InternalMmapVector<const char *> storage_ SANITIZER_GUARDED_BY(*mu_);
|
||||
} module_names_;
|
||||
|
||||
/// Platform-specific function for creating a Symbolizer object.
|
||||
@@ -220,7 +220,7 @@ class Symbolizer final {
|
||||
// always synchronized.
|
||||
Mutex mu_;
|
||||
|
||||
IntrusiveList<SymbolizerTool> tools_;
|
||||
IntrusiveList<SymbolizerTool> tools_ SANITIZER_GUARDED_BY(mu_);
|
||||
|
||||
explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace __sanitizer {
|
||||
bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
|
||||
Dl_info info;
|
||||
int result = dladdr((const void *)addr, &info);
|
||||
if (!result) return false;
|
||||
if (!result || !info.dli_sname) return false;
|
||||
|
||||
// Compute offset if possible. `dladdr()` doesn't always ensure that `addr >=
|
||||
// sym_addr` so only compute the offset when this holds. Failure to find the
|
||||
@@ -51,7 +51,7 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
|
||||
bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
|
||||
Dl_info info;
|
||||
int result = dladdr((const void *)addr, &info);
|
||||
if (!result) return false;
|
||||
if (!result || !info.dli_sname) return false;
|
||||
const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
|
||||
if (!demangled)
|
||||
demangled = info.dli_sname;
|
||||
|
||||
@@ -227,12 +227,15 @@ static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid,
|
||||
SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc,
|
||||
(void *)sig.bp, (void *)sig.sp, tid);
|
||||
Printf("%s", d.Default());
|
||||
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
|
||||
BufferedStackTrace *stack = stack_buffer.data();
|
||||
stack->Reset();
|
||||
unwind(sig, unwind_context, stack);
|
||||
stack->Print();
|
||||
ReportErrorSummary(kDescription, stack);
|
||||
// Avoid SEGVs in the unwinder when bp couldn't be determined.
|
||||
if (sig.bp) {
|
||||
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
|
||||
BufferedStackTrace *stack = stack_buffer.data();
|
||||
stack->Reset();
|
||||
unwind(sig, unwind_context, stack);
|
||||
stack->Print();
|
||||
ReportErrorSummary(kDescription, stack);
|
||||
}
|
||||
}
|
||||
|
||||
static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
|
||||
|
||||
@@ -65,12 +65,13 @@ void InitializeDbgHelpIfNeeded() {
|
||||
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
|
||||
CHECK(dbghelp && "failed to load dbghelp.dll");
|
||||
|
||||
#define DBGHELP_IMPORT(name) \
|
||||
do { \
|
||||
name = \
|
||||
reinterpret_cast<decltype(::name) *>(GetProcAddress(dbghelp, #name)); \
|
||||
CHECK(name != nullptr); \
|
||||
} while (0)
|
||||
# define DBGHELP_IMPORT(name) \
|
||||
do { \
|
||||
name = reinterpret_cast<decltype(::name) *>( \
|
||||
(void *)GetProcAddress(dbghelp, #name)); \
|
||||
CHECK(name != nullptr); \
|
||||
} while (0)
|
||||
|
||||
DBGHELP_IMPORT(StackWalk64);
|
||||
DBGHELP_IMPORT(SymCleanup);
|
||||
DBGHELP_IMPORT(SymFromAddr);
|
||||
|
||||
72
lib/tsan/sanitizer_common/sanitizer_thread_history.cpp
vendored
Normal file
72
lib/tsan/sanitizer_common/sanitizer_thread_history.cpp
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
//===-- sanitizer_thread_history.cpp --------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_thread_history.h"
|
||||
|
||||
#include "sanitizer_stackdepot.h"
|
||||
namespace __sanitizer {
|
||||
|
||||
void PrintThreadHistory(ThreadRegistry ®istry, InternalScopedString &out) {
|
||||
ThreadRegistryLock l(®istry);
|
||||
// Stack traces are largest part of printout and they often the same for
|
||||
// multiple threads, so we will deduplicate them.
|
||||
InternalMmapVector<const ThreadContextBase *> stacks;
|
||||
|
||||
registry.RunCallbackForEachThreadLocked(
|
||||
[](ThreadContextBase *context, void *arg) {
|
||||
static_cast<decltype(&stacks)>(arg)->push_back(context);
|
||||
},
|
||||
&stacks);
|
||||
|
||||
Sort(stacks.data(), stacks.size(),
|
||||
[](const ThreadContextBase *a, const ThreadContextBase *b) {
|
||||
if (a->stack_id < b->stack_id)
|
||||
return true;
|
||||
if (a->stack_id > b->stack_id)
|
||||
return false;
|
||||
return a->unique_id < b->unique_id;
|
||||
});
|
||||
|
||||
auto describe_thread = [&](const ThreadContextBase *context) {
|
||||
if (!context) {
|
||||
out.Append("T-1");
|
||||
return;
|
||||
}
|
||||
out.AppendF("T%llu/%llu", context->unique_id, context->os_id);
|
||||
if (internal_strlen(context->name))
|
||||
out.AppendF(" (%s)", context->name);
|
||||
};
|
||||
|
||||
auto get_parent =
|
||||
[&](const ThreadContextBase *context) -> const ThreadContextBase * {
|
||||
if (!context)
|
||||
return nullptr;
|
||||
ThreadContextBase *parent = registry.GetThreadLocked(context->parent_tid);
|
||||
if (!parent)
|
||||
return nullptr;
|
||||
if (parent->unique_id >= context->unique_id)
|
||||
return nullptr;
|
||||
return parent;
|
||||
};
|
||||
|
||||
const ThreadContextBase *prev = nullptr;
|
||||
for (const ThreadContextBase *context : stacks) {
|
||||
if (prev && prev->stack_id != context->stack_id)
|
||||
StackDepotGet(prev->stack_id).PrintTo(&out);
|
||||
prev = context;
|
||||
out.Append("Thread ");
|
||||
describe_thread(context);
|
||||
out.Append(" was created by ");
|
||||
describe_thread(get_parent(context));
|
||||
out.Append("\n");
|
||||
}
|
||||
if (prev)
|
||||
StackDepotGet(prev->stack_id).PrintTo(&out);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
24
lib/tsan/sanitizer_common/sanitizer_thread_history.h
vendored
Normal file
24
lib/tsan/sanitizer_common/sanitizer_thread_history.h
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
//===-- sanitizer_thread_history.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Utility to print thread histroy from ThreadRegistry.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_THREAD_HISTORY_H
|
||||
#define SANITIZER_THREAD_HISTORY_H
|
||||
|
||||
#include "sanitizer_thread_registry.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void PrintThreadHistory(ThreadRegistry& registry, InternalScopedString& out);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_THREAD_HISTORY_H
|
||||
@@ -18,9 +18,17 @@
|
||||
namespace __sanitizer {
|
||||
|
||||
ThreadContextBase::ThreadContextBase(u32 tid)
|
||||
: tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
|
||||
status(ThreadStatusInvalid), detached(false),
|
||||
thread_type(ThreadType::Regular), parent_tid(0), next(0) {
|
||||
: tid(tid),
|
||||
unique_id(0),
|
||||
reuse_count(),
|
||||
os_id(0),
|
||||
user_id(0),
|
||||
status(ThreadStatusInvalid),
|
||||
detached(false),
|
||||
thread_type(ThreadType::Regular),
|
||||
parent_tid(0),
|
||||
stack_id(0),
|
||||
next(0) {
|
||||
name[0] = '\0';
|
||||
atomic_store(&thread_destroyed, 0, memory_order_release);
|
||||
}
|
||||
@@ -39,8 +47,7 @@ void ThreadContextBase::SetName(const char *new_name) {
|
||||
}
|
||||
|
||||
void ThreadContextBase::SetDead() {
|
||||
CHECK(status == ThreadStatusRunning ||
|
||||
status == ThreadStatusFinished);
|
||||
CHECK(status == ThreadStatusRunning || status == ThreadStatusFinished);
|
||||
status = ThreadStatusDead;
|
||||
user_id = 0;
|
||||
OnDead();
|
||||
@@ -68,7 +75,8 @@ void ThreadContextBase::SetFinished() {
|
||||
// for a thread that never actually started. In that case the thread
|
||||
// should go to ThreadStatusFinished regardless of whether it was created
|
||||
// as detached.
|
||||
if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished;
|
||||
if (!detached || status == ThreadStatusCreated)
|
||||
status = ThreadStatusFinished;
|
||||
OnFinished();
|
||||
}
|
||||
|
||||
@@ -81,14 +89,17 @@ void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type,
|
||||
}
|
||||
|
||||
void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
|
||||
bool _detached, u32 _parent_tid, void *arg) {
|
||||
bool _detached, u32 _parent_tid,
|
||||
u32 _stack_tid, void *arg) {
|
||||
status = ThreadStatusCreated;
|
||||
user_id = _user_id;
|
||||
unique_id = _unique_id;
|
||||
detached = _detached;
|
||||
// Parent tid makes no sense for the main thread.
|
||||
if (tid != kMainTid)
|
||||
if (tid != kMainTid) {
|
||||
parent_tid = _parent_tid;
|
||||
stack_id = _stack_tid;
|
||||
}
|
||||
OnCreated(arg);
|
||||
}
|
||||
|
||||
@@ -124,8 +135,10 @@ void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
|
||||
ThreadRegistryLock l(this);
|
||||
if (total)
|
||||
*total = threads_.size();
|
||||
if (running) *running = running_threads_;
|
||||
if (alive) *alive = alive_threads_;
|
||||
if (running)
|
||||
*running = running_threads_;
|
||||
if (alive)
|
||||
*alive = alive_threads_;
|
||||
}
|
||||
|
||||
uptr ThreadRegistry::GetMaxAliveThreads() {
|
||||
@@ -134,7 +147,7 @@ uptr ThreadRegistry::GetMaxAliveThreads() {
|
||||
}
|
||||
|
||||
u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
|
||||
void *arg) {
|
||||
u32 stack_tid, void *arg) {
|
||||
ThreadRegistryLock l(this);
|
||||
u32 tid = kInvalidTid;
|
||||
ThreadContextBase *tctx = QuarantinePop();
|
||||
@@ -150,8 +163,10 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
|
||||
Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
|
||||
SanitizerToolName, max_threads_);
|
||||
#else
|
||||
Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
|
||||
" dying\n", max_threads_);
|
||||
Printf(
|
||||
"race: limit on %u simultaneously alive goroutines is exceeded,"
|
||||
" dying\n",
|
||||
max_threads_);
|
||||
#endif
|
||||
Die();
|
||||
}
|
||||
@@ -170,8 +185,8 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
|
||||
// positives later (e.g. if we join a wrong thread).
|
||||
CHECK(live_.try_emplace(user_id, tid).second);
|
||||
}
|
||||
tctx->SetCreated(user_id, total_threads_++, detached,
|
||||
parent_tid, arg);
|
||||
tctx->SetCreated(user_id, total_threads_++, detached, parent_tid, stack_tid,
|
||||
arg);
|
||||
return tid;
|
||||
}
|
||||
|
||||
@@ -196,8 +211,8 @@ u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
|
||||
return kInvalidTid;
|
||||
}
|
||||
|
||||
ThreadContextBase *
|
||||
ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
|
||||
ThreadContextBase *ThreadRegistry::FindThreadContextLocked(
|
||||
FindThreadCallback cb, void *arg) {
|
||||
CheckLocked();
|
||||
for (u32 tid = 0; tid < threads_.size(); tid++) {
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
@@ -210,7 +225,7 @@ ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
|
||||
static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
|
||||
void *arg) {
|
||||
return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
|
||||
tctx->status != ThreadStatusDead);
|
||||
tctx->status != ThreadStatusDead);
|
||||
}
|
||||
|
||||
ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
|
||||
|
||||
@@ -52,6 +52,7 @@ class ThreadContextBase {
|
||||
ThreadType thread_type;
|
||||
|
||||
u32 parent_tid;
|
||||
u32 stack_id;
|
||||
ThreadContextBase *next; // For storing thread contexts in a list.
|
||||
|
||||
atomic_uint32_t thread_destroyed; // To address race of Joined vs Finished
|
||||
@@ -63,7 +64,7 @@ class ThreadContextBase {
|
||||
void SetFinished();
|
||||
void SetStarted(tid_t _os_id, ThreadType _thread_type, void *arg);
|
||||
void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
|
||||
u32 _parent_tid, void *arg);
|
||||
u32 _parent_tid, u32 _stack_tid, void *arg);
|
||||
void Reset();
|
||||
|
||||
void SetDestroyed();
|
||||
@@ -101,12 +102,16 @@ class SANITIZER_MUTEX ThreadRegistry {
|
||||
|
||||
// Should be guarded by ThreadRegistryLock.
|
||||
ThreadContextBase *GetThreadLocked(u32 tid) {
|
||||
return threads_.empty() ? nullptr : threads_[tid];
|
||||
return tid < threads_.size() ? threads_[tid] : nullptr;
|
||||
}
|
||||
|
||||
u32 NumThreadsLocked() const { return threads_.size(); }
|
||||
|
||||
u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg);
|
||||
u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, u32 stack_tid,
|
||||
void *arg);
|
||||
u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg) {
|
||||
return CreateThread(user_id, detached, parent_tid, 0, arg);
|
||||
}
|
||||
|
||||
typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg);
|
||||
// Invokes callback with a specified arg for each thread context.
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_platform_interceptors.h"
|
||||
|
||||
@@ -66,7 +68,7 @@ static DTLS::DTVBlock *DTLS_NextBlock(atomic_uintptr_t *cur) {
|
||||
}
|
||||
|
||||
static DTLS::DTV *DTLS_Find(uptr id) {
|
||||
VReport(2, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id);
|
||||
VReport(3, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id);
|
||||
static constexpr uptr kPerBlock = ARRAY_SIZE(DTLS::DTVBlock::dtvs);
|
||||
DTLS::DTVBlock *cur = DTLS_NextBlock(&dtls.dtv_block);
|
||||
if (!cur)
|
||||
@@ -110,6 +112,21 @@ SANITIZER_WEAK_ATTRIBUTE
|
||||
const void *__sanitizer_get_allocated_begin(const void *p);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_WEAK_DEF(uptr, __sanitizer_get_dtls_size,
|
||||
const void *tls_begin) {
|
||||
const void *start = __sanitizer_get_allocated_begin(tls_begin);
|
||||
if (!start)
|
||||
return 0;
|
||||
CHECK_LE(start, tls_begin);
|
||||
uptr tls_size = __sanitizer_get_allocated_size(start);
|
||||
VReport(2, "__tls_get_addr: glibc DTLS suspected; tls={%p,0x%zx}\n",
|
||||
tls_begin, tls_size);
|
||||
uptr offset =
|
||||
(reinterpret_cast<uptr>(tls_begin) - reinterpret_cast<uptr>(start));
|
||||
CHECK_LE(offset, tls_size);
|
||||
return tls_size - offset;
|
||||
}
|
||||
|
||||
DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
|
||||
uptr static_tls_begin, uptr static_tls_end) {
|
||||
if (!common_flags()->intercept_tls_get_addr) return 0;
|
||||
@@ -117,8 +134,8 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
|
||||
uptr dso_id = arg->dso_id;
|
||||
DTLS::DTV *dtv = DTLS_Find(dso_id);
|
||||
if (!dtv || dtv->beg)
|
||||
return 0;
|
||||
uptr tls_size = 0;
|
||||
return nullptr;
|
||||
CHECK_LE(static_tls_begin, static_tls_end);
|
||||
uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
|
||||
VReport(2,
|
||||
"__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: %p; sp: %p "
|
||||
@@ -126,36 +143,33 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
|
||||
(void *)arg, arg->dso_id, arg->offset, res, (void *)tls_beg,
|
||||
(void *)&tls_beg,
|
||||
atomic_load(&number_of_live_dtls, memory_order_relaxed));
|
||||
if (dtls.last_memalign_ptr == tls_beg) {
|
||||
tls_size = dtls.last_memalign_size;
|
||||
VReport(2, "__tls_get_addr: glibc <=2.24 suspected; tls={%p,0x%zx}\n",
|
||||
(void *)tls_beg, tls_size);
|
||||
} else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
|
||||
if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
|
||||
// This is the static TLS block which was initialized / unpoisoned at thread
|
||||
// creation.
|
||||
VReport(2, "__tls_get_addr: static tls: %p\n", (void *)tls_beg);
|
||||
tls_size = 0;
|
||||
} else if (const void *start =
|
||||
__sanitizer_get_allocated_begin((void *)tls_beg)) {
|
||||
tls_beg = (uptr)start;
|
||||
tls_size = __sanitizer_get_allocated_size(start);
|
||||
VReport(2, "__tls_get_addr: glibc >=2.25 suspected; tls={%p,0x%zx}\n",
|
||||
(void *)tls_beg, tls_size);
|
||||
} else {
|
||||
VReport(2, "__tls_get_addr: Can't guess glibc version\n");
|
||||
// This may happen inside the DTOR of main thread, so just ignore it.
|
||||
tls_size = 0;
|
||||
dtv->beg = tls_beg;
|
||||
dtv->size = 0;
|
||||
return nullptr;
|
||||
}
|
||||
if (uptr tls_size =
|
||||
__sanitizer_get_dtls_size(reinterpret_cast<void *>(tls_beg))) {
|
||||
dtv->beg = tls_beg;
|
||||
dtv->size = tls_size;
|
||||
return dtv;
|
||||
}
|
||||
VReport(2, "__tls_get_addr: Can't guess glibc version\n");
|
||||
// This may happen inside the DTOR a thread, or async signal handlers before
|
||||
// thread initialization, so just ignore it.
|
||||
//
|
||||
// If the unknown block is dynamic TLS, unlikely we will be able to recognize
|
||||
// it in future, mark it as done with '{tls_beg, 0}'.
|
||||
//
|
||||
// If the block is static TLS, possible reason of failed detection is nullptr
|
||||
// in `static_tls_begin`. Regardless of reasons, the future handling of static
|
||||
// TLS is still '{tls_beg, 0}'.
|
||||
dtv->beg = tls_beg;
|
||||
dtv->size = tls_size;
|
||||
return dtv;
|
||||
}
|
||||
|
||||
void DTLS_on_libc_memalign(void *ptr, uptr size) {
|
||||
if (!common_flags()->intercept_tls_get_addr) return;
|
||||
VReport(2, "DTLS_on_libc_memalign: %p 0x%zx\n", ptr, size);
|
||||
dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
|
||||
dtls.last_memalign_size = size;
|
||||
dtv->size = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DTLS *DTLS_Get() { return &dtls; }
|
||||
@@ -166,7 +180,9 @@ bool DTLSInDestruction(DTLS *dtls) {
|
||||
}
|
||||
|
||||
#else
|
||||
void DTLS_on_libc_memalign(void *ptr, uptr size) {}
|
||||
SANITIZER_INTERFACE_WEAK_DEF(uptr, __sanitizer_get_dtls_size, const void *) {
|
||||
return 0;
|
||||
}
|
||||
DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res,
|
||||
unsigned long, unsigned long) { return 0; }
|
||||
DTLS *DTLS_Get() { return 0; }
|
||||
|
||||
@@ -55,10 +55,6 @@ struct DTLS {
|
||||
static_assert(sizeof(DTVBlock) <= 4096UL, "Unexpected block size");
|
||||
|
||||
atomic_uintptr_t dtv_block;
|
||||
|
||||
// Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cpp
|
||||
uptr last_memalign_size;
|
||||
uptr last_memalign_ptr;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
|
||||
101
lib/tsan/sanitizer_common/sanitizer_win.cpp
vendored
101
lib/tsan/sanitizer_common/sanitizer_win.cpp
vendored
@@ -164,7 +164,24 @@ void UnmapOrDie(void *addr, uptr size, bool raw_report) {
|
||||
static void *ReturnNullptrOnOOMOrDie(uptr size, const char *mem_type,
|
||||
const char *mmap_type) {
|
||||
error_t last_error = GetLastError();
|
||||
if (last_error == ERROR_NOT_ENOUGH_MEMORY)
|
||||
|
||||
// Assumption: VirtualAlloc is the last system call that was invoked before
|
||||
// this method.
|
||||
// VirtualAlloc emits one of 3 error codes when running out of memory
|
||||
// 1. ERROR_NOT_ENOUGH_MEMORY:
|
||||
// There's not enough memory to execute the command
|
||||
// 2. ERROR_INVALID_PARAMETER:
|
||||
// VirtualAlloc will return this if the request would allocate memory at an
|
||||
// address exceeding or being very close to the maximum application address
|
||||
// (the `lpMaximumApplicationAddress` field within the `SystemInfo` struct).
|
||||
// This does not seem to be officially documented, but is corroborated here:
|
||||
// https://stackoverflow.com/questions/45833674/why-does-virtualalloc-fail-for-lpaddress-greater-than-0x6ffffffffff
|
||||
// 3. ERROR_COMMITMENT_LIMIT:
|
||||
// VirtualAlloc will return this if e.g. the pagefile is too small to commit
|
||||
// the requested amount of memory.
|
||||
if (last_error == ERROR_NOT_ENOUGH_MEMORY ||
|
||||
last_error == ERROR_INVALID_PARAMETER ||
|
||||
last_error == ERROR_COMMITMENT_LIMIT)
|
||||
return nullptr;
|
||||
ReportMmapFailureAndDie(size, mem_type, mmap_type, last_error);
|
||||
}
|
||||
@@ -873,24 +890,18 @@ uptr GetTlsSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitTlsSize() {
|
||||
}
|
||||
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
uptr *tls_addr, uptr *tls_size) {
|
||||
#if SANITIZER_GO
|
||||
*stk_addr = 0;
|
||||
*stk_size = 0;
|
||||
*tls_addr = 0;
|
||||
*tls_size = 0;
|
||||
#else
|
||||
uptr stack_top, stack_bottom;
|
||||
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
|
||||
*stk_addr = stack_bottom;
|
||||
*stk_size = stack_top - stack_bottom;
|
||||
*tls_addr = 0;
|
||||
*tls_size = 0;
|
||||
#endif
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
|
||||
uptr *tls_begin, uptr *tls_end) {
|
||||
# if SANITIZER_GO
|
||||
*stk_begin = 0;
|
||||
*stk_end = 0;
|
||||
*tls_begin = 0;
|
||||
*tls_end = 0;
|
||||
# else
|
||||
GetThreadStackTopAndBottom(main, stk_end, stk_begin);
|
||||
*tls_begin = 0;
|
||||
*tls_end = 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
void ReportFile::Write(const char *buffer, uptr length) {
|
||||
@@ -974,6 +985,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryMemCpy(void *dest, const void *src, uptr n) {
|
||||
// TODO: implement.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SignalContext::IsStackOverflow() const {
|
||||
return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW;
|
||||
}
|
||||
@@ -1039,7 +1055,52 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
|
||||
}
|
||||
|
||||
void SignalContext::DumpAllRegisters(void *context) {
|
||||
// FIXME: Implement this.
|
||||
CONTEXT *ctx = (CONTEXT *)context;
|
||||
# if defined(_M_X64)
|
||||
Report("Register values:\n");
|
||||
Printf("rax = %llx ", ctx->Rax);
|
||||
Printf("rbx = %llx ", ctx->Rbx);
|
||||
Printf("rcx = %llx ", ctx->Rcx);
|
||||
Printf("rdx = %llx ", ctx->Rdx);
|
||||
Printf("\n");
|
||||
Printf("rdi = %llx ", ctx->Rdi);
|
||||
Printf("rsi = %llx ", ctx->Rsi);
|
||||
Printf("rbp = %llx ", ctx->Rbp);
|
||||
Printf("rsp = %llx ", ctx->Rsp);
|
||||
Printf("\n");
|
||||
Printf("r8 = %llx ", ctx->R8);
|
||||
Printf("r9 = %llx ", ctx->R9);
|
||||
Printf("r10 = %llx ", ctx->R10);
|
||||
Printf("r11 = %llx ", ctx->R11);
|
||||
Printf("\n");
|
||||
Printf("r12 = %llx ", ctx->R12);
|
||||
Printf("r13 = %llx ", ctx->R13);
|
||||
Printf("r14 = %llx ", ctx->R14);
|
||||
Printf("r15 = %llx ", ctx->R15);
|
||||
Printf("\n");
|
||||
# elif defined(_M_IX86)
|
||||
Report("Register values:\n");
|
||||
Printf("eax = %lx ", ctx->Eax);
|
||||
Printf("ebx = %lx ", ctx->Ebx);
|
||||
Printf("ecx = %lx ", ctx->Ecx);
|
||||
Printf("edx = %lx ", ctx->Edx);
|
||||
Printf("\n");
|
||||
Printf("edi = %lx ", ctx->Edi);
|
||||
Printf("esi = %lx ", ctx->Esi);
|
||||
Printf("ebp = %lx ", ctx->Ebp);
|
||||
Printf("esp = %lx ", ctx->Esp);
|
||||
Printf("\n");
|
||||
# elif defined(_M_ARM64)
|
||||
Report("Register values:\n");
|
||||
for (int i = 0; i <= 30; i++) {
|
||||
Printf("x%d%s = %llx", i < 10 ? " " : "", ctx->X[i]);
|
||||
if (i % 4 == 3)
|
||||
Printf("\n");
|
||||
}
|
||||
# else
|
||||
// TODO
|
||||
(void)ctx;
|
||||
# endif
|
||||
}
|
||||
|
||||
int SignalContext::GetType() const {
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
//===-- sanitizer_win_dll_thunk.cpp ---------------------------------------===//
|
||||
//
|
||||
// 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 defines a family of thunks that should be statically linked into
|
||||
// the DLLs that have instrumentation in order to delegate the calls to the
|
||||
// shared runtime that lives in the main binary.
|
||||
// See https://github.com/google/sanitizers/issues/209 for the details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifdef SANITIZER_DLL_THUNK
|
||||
#include "sanitizer_win_defs.h"
|
||||
#include "sanitizer_win_dll_thunk.h"
|
||||
#include "interception/interception.h"
|
||||
|
||||
extern "C" {
|
||||
void *WINAPI GetModuleHandleA(const char *module_name);
|
||||
void abort();
|
||||
}
|
||||
|
||||
namespace __sanitizer {
|
||||
uptr dllThunkGetRealAddrOrDie(const char *name) {
|
||||
uptr ret =
|
||||
__interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
|
||||
if (!ret)
|
||||
abort();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dllThunkIntercept(const char* main_function, uptr dll_function) {
|
||||
uptr wrapper = dllThunkGetRealAddrOrDie(main_function);
|
||||
if (!__interception::OverrideFunction(dll_function, wrapper, 0))
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dllThunkInterceptWhenPossible(const char* main_function,
|
||||
const char* default_function, uptr dll_function) {
|
||||
uptr wrapper = __interception::InternalGetProcAddress(
|
||||
(void *)GetModuleHandleA(0), main_function);
|
||||
if (!wrapper)
|
||||
wrapper = dllThunkGetRealAddrOrDie(default_function);
|
||||
if (!__interception::OverrideFunction(dll_function, wrapper, 0))
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
||||
// Include Sanitizer Common interface.
|
||||
#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
|
||||
#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
|
||||
#include "sanitizer_common_interface.inc"
|
||||
|
||||
#pragma section(".DLLTH$A", read)
|
||||
#pragma section(".DLLTH$Z", read)
|
||||
|
||||
typedef void (*DllThunkCB)();
|
||||
extern "C" {
|
||||
__declspec(allocate(".DLLTH$A")) DllThunkCB __start_dll_thunk;
|
||||
__declspec(allocate(".DLLTH$Z")) DllThunkCB __stop_dll_thunk;
|
||||
}
|
||||
|
||||
// Disable compiler warnings that show up if we declare our own version
|
||||
// of a compiler intrinsic (e.g. strlen).
|
||||
#pragma warning(disable: 4391)
|
||||
#pragma warning(disable: 4392)
|
||||
|
||||
extern "C" int __dll_thunk_init() {
|
||||
static bool flag = false;
|
||||
// __dll_thunk_init is expected to be called by only one thread.
|
||||
if (flag) return 0;
|
||||
flag = true;
|
||||
|
||||
for (DllThunkCB *it = &__start_dll_thunk; it < &__stop_dll_thunk; ++it)
|
||||
if (*it)
|
||||
(*it)();
|
||||
|
||||
// In DLLs, the callbacks are expected to return 0,
|
||||
// otherwise CRT initialization fails.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We want to call dll_thunk_init before C/C++ initializers / constructors are
|
||||
// executed, otherwise functions like memset might be invoked.
|
||||
#pragma section(".CRT$XIB", long, read)
|
||||
__declspec(allocate(".CRT$XIB")) int (*__dll_thunk_preinit)() =
|
||||
__dll_thunk_init;
|
||||
|
||||
static void WINAPI dll_thunk_thread_init(void *mod, unsigned long reason,
|
||||
void *reserved) {
|
||||
if (reason == /*DLL_PROCESS_ATTACH=*/1) __dll_thunk_init();
|
||||
}
|
||||
|
||||
#pragma section(".CRT$XLAB", long, read)
|
||||
__declspec(allocate(".CRT$XLAB")) void (WINAPI *__dll_thunk_tls_init)(void *,
|
||||
unsigned long, void *) = dll_thunk_thread_init;
|
||||
|
||||
#endif // SANITIZER_DLL_THUNK
|
||||
181
lib/tsan/sanitizer_common/sanitizer_win_dll_thunk.h
vendored
181
lib/tsan/sanitizer_common/sanitizer_win_dll_thunk.h
vendored
@@ -1,181 +0,0 @@
|
||||
//===-- sanitizer_win_dll_thunk.h -----------------------------------------===//
|
||||
//
|
||||
// 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 header provide helper macros to delegate calls to the shared runtime
|
||||
// that lives in the main executable. It should be included to dll_thunks that
|
||||
// will be linked to the dlls, when the sanitizer is a static library included
|
||||
// in the main executable.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_WIN_DLL_THUNK_H
|
||||
#define SANITIZER_WIN_DLL_THUNK_H
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
uptr dllThunkGetRealAddrOrDie(const char *name);
|
||||
|
||||
int dllThunkIntercept(const char* main_function, uptr dll_function);
|
||||
|
||||
int dllThunkInterceptWhenPossible(const char* main_function,
|
||||
const char* default_function, uptr dll_function);
|
||||
}
|
||||
|
||||
extern "C" int __dll_thunk_init();
|
||||
|
||||
// ----------------- Function interception helper macros -------------------- //
|
||||
// Override dll_function with main_function from main executable.
|
||||
#define INTERCEPT_OR_DIE(main_function, dll_function) \
|
||||
static int intercept_##dll_function() { \
|
||||
return __sanitizer::dllThunkIntercept(main_function, (__sanitizer::uptr) \
|
||||
dll_function); \
|
||||
} \
|
||||
__pragma(section(".DLLTH$M", long, read)) \
|
||||
__declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \
|
||||
intercept_##dll_function;
|
||||
|
||||
// Try to override dll_function with main_function from main executable.
|
||||
// If main_function is not present, override dll_function with default_function.
|
||||
#define INTERCEPT_WHEN_POSSIBLE(main_function, default_function, dll_function) \
|
||||
static int intercept_##dll_function() { \
|
||||
return __sanitizer::dllThunkInterceptWhenPossible(main_function, \
|
||||
default_function, (__sanitizer::uptr)dll_function); \
|
||||
} \
|
||||
__pragma(section(".DLLTH$M", long, read)) \
|
||||
__declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \
|
||||
intercept_##dll_function;
|
||||
|
||||
// -------------------- Function interception macros ------------------------ //
|
||||
// Special case of hooks -- ASan own interface functions. Those are only called
|
||||
// after __asan_init, thus an empty implementation is sufficient.
|
||||
#define INTERCEPT_SANITIZER_FUNCTION(name) \
|
||||
extern "C" __declspec(noinline) void name() { \
|
||||
volatile int prevent_icf = (__LINE__ << 8) ^ __COUNTER__; \
|
||||
static const char function_name[] = #name; \
|
||||
for (const char* ptr = &function_name[0]; *ptr; ++ptr) \
|
||||
prevent_icf ^= *ptr; \
|
||||
(void)prevent_icf; \
|
||||
__debugbreak(); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name)
|
||||
|
||||
// Special case of hooks -- Weak functions, could be redefined in the main
|
||||
// executable, but that is not necessary, so we shouldn't die if we can not find
|
||||
// a reference. Instead, when the function is not present in the main executable
|
||||
// we consider the default impl provided by asan library.
|
||||
#define INTERCEPT_SANITIZER_WEAK_FUNCTION(name) \
|
||||
extern "C" __declspec(noinline) void name() { \
|
||||
volatile int prevent_icf = (__LINE__ << 8) ^ __COUNTER__; \
|
||||
static const char function_name[] = #name; \
|
||||
for (const char* ptr = &function_name[0]; *ptr; ++ptr) \
|
||||
prevent_icf ^= *ptr; \
|
||||
(void)prevent_icf; \
|
||||
__debugbreak(); \
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, STRINGIFY(WEAK_EXPORT_NAME(name)), name)
|
||||
|
||||
// We can't define our own version of strlen etc. because that would lead to
|
||||
// link-time or even type mismatch errors. Instead, we can declare a function
|
||||
// just to be able to get its address. Me may miss the first few calls to the
|
||||
// functions since it can be called before __dll_thunk_init, but that would lead
|
||||
// to false negatives in the startup code before user's global initializers,
|
||||
// which isn't a big deal.
|
||||
#define INTERCEPT_LIBRARY_FUNCTION(name) \
|
||||
extern "C" void name(); \
|
||||
INTERCEPT_OR_DIE(STRINGIFY(WRAP(name)), name)
|
||||
|
||||
// Use these macros for functions that could be called before __dll_thunk_init()
|
||||
// is executed and don't lead to errors if defined (free, malloc, etc).
|
||||
#define INTERCEPT_WRAP_V_V(name) \
|
||||
extern "C" void name() { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
fn(); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_V_W(name) \
|
||||
extern "C" void name(void *arg) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
fn(arg); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_V_WW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
fn(arg1, arg2); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_V_WWW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
fn(arg1, arg2, arg3); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_V(name) \
|
||||
extern "C" void *name() { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_W(name) \
|
||||
extern "C" void *name(void *arg) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(arg); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_WW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(arg1, arg2); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_WWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_WWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_WWWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
||||
void *arg5) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#define INTERCEPT_WRAP_W_WWWWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
||||
void *arg5, void *arg6) { \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
} \
|
||||
INTERCEPT_OR_DIE(#name, name);
|
||||
|
||||
#endif // SANITIZER_WIN_DLL_THUNK_H
|
||||
@@ -1,26 +0,0 @@
|
||||
//===-- santizer_win_dynamic_runtime_thunk.cpp ----------------------------===//
|
||||
//
|
||||
// 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 defines things that need to be present in the application modules
|
||||
// to interact with Sanitizer Common, when it is included in a dll.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
|
||||
#define SANITIZER_IMPORT_INTERFACE 1
|
||||
#include "sanitizer_win_defs.h"
|
||||
// Define weak alias for all weak functions imported from sanitizer common.
|
||||
#define INTERFACE_FUNCTION(Name)
|
||||
#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
|
||||
#include "sanitizer_common_interface.inc"
|
||||
#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
|
||||
|
||||
namespace __sanitizer {
|
||||
// Add one, otherwise unused, external symbol to this object file so that the
|
||||
// Visual C++ linker includes it and reads the .drective section.
|
||||
void ForceWholeArchiveIncludeForSanitizerCommon() {}
|
||||
}
|
||||
71
lib/tsan/sanitizer_common/sanitizer_win_immortalize.h
vendored
Normal file
71
lib/tsan/sanitizer_common/sanitizer_win_immortalize.h
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//===-- sanitizer_win_immortalize.h ---------------------------------------===//
|
||||
//
|
||||
// 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 shared between AddressSanitizer, and interception.
|
||||
//
|
||||
// Windows-specific thread-safe and pre-CRT global initialization safe
|
||||
// infrastructure to create an object whose destructor is never called.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#if SANITIZER_WINDOWS
|
||||
# pragma once
|
||||
// Requires including sanitizer_placement_new.h (which is not allowed to be
|
||||
// included in headers).
|
||||
|
||||
# include "sanitizer_win_defs.h"
|
||||
// These types are required to satisfy XFG which requires that the names of the
|
||||
// types for indirect calls to be correct as well as the name of the original
|
||||
// type for any typedefs.
|
||||
|
||||
// TODO: There must be a better way to do this
|
||||
# ifndef _WINDOWS_
|
||||
typedef void* PVOID;
|
||||
typedef int BOOL;
|
||||
typedef union _RTL_RUN_ONCE {
|
||||
PVOID ptr;
|
||||
} INIT_ONCE, *PINIT_ONCE;
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllimport) int WINAPI InitOnceExecuteOnce(
|
||||
PINIT_ONCE, BOOL(WINAPI*)(PINIT_ONCE, PVOID, PVOID*), void*, void*);
|
||||
}
|
||||
# endif
|
||||
|
||||
namespace __sanitizer {
|
||||
template <class Ty>
|
||||
BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr, PVOID*) noexcept {
|
||||
// Ty must provide a placement new operator
|
||||
new (storage_ptr) Ty();
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class Ty, typename Arg>
|
||||
BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr,
|
||||
PVOID* param) noexcept {
|
||||
// Ty must provide a placement new operator
|
||||
new (storage_ptr) Ty(*((Arg*)param));
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class Ty>
|
||||
Ty& immortalize() { // return a reference to an object that will live forever
|
||||
static INIT_ONCE flag;
|
||||
alignas(Ty) static unsigned char storage[sizeof(Ty)];
|
||||
InitOnceExecuteOnce(&flag, immortalize_impl<Ty>, &storage, nullptr);
|
||||
return reinterpret_cast<Ty&>(storage);
|
||||
}
|
||||
|
||||
template <class Ty, typename Arg>
|
||||
Ty& immortalize(
|
||||
Arg arg) { // return a reference to an object that will live forever
|
||||
static INIT_ONCE flag;
|
||||
alignas(Ty) static unsigned char storage[sizeof(Ty)];
|
||||
InitOnceExecuteOnce(&flag, immortalize_impl<Ty, Arg>, &storage, &arg);
|
||||
return reinterpret_cast<Ty&>(storage);
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
#endif // SANITIZER_WINDOWS
|
||||
156
lib/tsan/sanitizer_common/sanitizer_win_interception.cpp
vendored
Normal file
156
lib/tsan/sanitizer_common/sanitizer_win_interception.cpp
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
//===-- sanitizer_win_interception.cpp -------------------- --*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Windows-specific export surface to provide interception for parts of the
|
||||
// runtime that are always statically linked, both for overriding user-defined
|
||||
// functions as well as registering weak functions that the ASAN runtime should
|
||||
// use over defaults.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_WINDOWS
|
||||
# include <stddef.h>
|
||||
|
||||
# include "interception/interception.h"
|
||||
# include "sanitizer_addrhashmap.h"
|
||||
# include "sanitizer_common.h"
|
||||
# include "sanitizer_internal_defs.h"
|
||||
# include "sanitizer_placement_new.h"
|
||||
# include "sanitizer_win_immortalize.h"
|
||||
# include "sanitizer_win_interception.h"
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
extern "C" void *__ImageBase;
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
static uptr GetSanitizerDllExport(const char *export_name) {
|
||||
const uptr function_address =
|
||||
__interception::InternalGetProcAddress(&__ImageBase, export_name);
|
||||
if (function_address == 0) {
|
||||
Report("ERROR: Failed to find sanitizer DLL export '%s'\n", export_name);
|
||||
CHECK("Failed to find sanitizer DLL export" && 0);
|
||||
}
|
||||
return function_address;
|
||||
}
|
||||
|
||||
struct WeakCallbackList {
|
||||
explicit constexpr WeakCallbackList(RegisterWeakFunctionCallback cb)
|
||||
: callback(cb), next(nullptr) {}
|
||||
|
||||
static void *operator new(size_t size) { return InternalAlloc(size); }
|
||||
|
||||
static void operator delete(void *p) { InternalFree(p); }
|
||||
|
||||
RegisterWeakFunctionCallback callback;
|
||||
WeakCallbackList *next;
|
||||
};
|
||||
using WeakCallbackMap = AddrHashMap<WeakCallbackList *, 11>;
|
||||
|
||||
static WeakCallbackMap *GetWeakCallbackMap() {
|
||||
return &immortalize<WeakCallbackMap>();
|
||||
}
|
||||
|
||||
void AddRegisterWeakFunctionCallback(uptr export_address,
|
||||
RegisterWeakFunctionCallback cb) {
|
||||
WeakCallbackMap::Handle h_find_or_create(GetWeakCallbackMap(), export_address,
|
||||
false, true);
|
||||
CHECK(h_find_or_create.exists());
|
||||
if (h_find_or_create.created()) {
|
||||
*h_find_or_create = new WeakCallbackList(cb);
|
||||
} else {
|
||||
(*h_find_or_create)->next = new WeakCallbackList(cb);
|
||||
}
|
||||
}
|
||||
|
||||
static void RunWeakFunctionCallbacks(uptr export_address) {
|
||||
WeakCallbackMap::Handle h_find(GetWeakCallbackMap(), export_address, false,
|
||||
false);
|
||||
if (!h_find.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WeakCallbackList *list = *h_find;
|
||||
do {
|
||||
list->callback();
|
||||
} while ((list = list->next));
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
extern "C" __declspec(dllexport) bool __cdecl __sanitizer_override_function(
|
||||
const char *export_name, const uptr user_function,
|
||||
uptr *const old_user_function) {
|
||||
CHECK(export_name);
|
||||
CHECK(user_function);
|
||||
|
||||
const uptr sanitizer_function = GetSanitizerDllExport(export_name);
|
||||
|
||||
const bool function_overridden = __interception::OverrideFunction(
|
||||
user_function, sanitizer_function, old_user_function);
|
||||
if (!function_overridden) {
|
||||
Report(
|
||||
"ERROR: Failed to override local function at '%p' with sanitizer "
|
||||
"function '%s'\n",
|
||||
user_function, export_name);
|
||||
CHECK("Failed to replace local function with sanitizer version." && 0);
|
||||
}
|
||||
|
||||
return function_overridden;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) bool __cdecl __sanitizer_override_function_by_addr(
|
||||
const uptr source_function, const uptr target_function,
|
||||
uptr *const old_target_function) {
|
||||
CHECK(source_function);
|
||||
CHECK(target_function);
|
||||
|
||||
const bool function_overridden = __interception::OverrideFunction(
|
||||
target_function, source_function, old_target_function);
|
||||
if (!function_overridden) {
|
||||
Report(
|
||||
"ERROR: Failed to override function at '%p' with function at "
|
||||
"'%p'\n",
|
||||
target_function, source_function);
|
||||
CHECK("Failed to apply function override." && 0);
|
||||
}
|
||||
|
||||
return function_overridden;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) bool __cdecl __sanitizer_register_weak_function(
|
||||
const char *export_name, const uptr user_function,
|
||||
uptr *const old_user_function) {
|
||||
CHECK(export_name);
|
||||
CHECK(user_function);
|
||||
|
||||
const uptr sanitizer_function = GetSanitizerDllExport(export_name);
|
||||
|
||||
const bool function_overridden = __interception::OverrideFunction(
|
||||
sanitizer_function, user_function, old_user_function);
|
||||
if (!function_overridden) {
|
||||
Report(
|
||||
"ERROR: Failed to register local function at '%p' to be used in "
|
||||
"place of sanitizer function '%s'\n.",
|
||||
user_function, export_name);
|
||||
CHECK("Failed to register weak function." && 0);
|
||||
}
|
||||
|
||||
// Note that thread-safety of RunWeakFunctionCallbacks in InitializeFlags
|
||||
// depends on __sanitizer_register_weak_functions being called during the
|
||||
// loader lock.
|
||||
RunWeakFunctionCallbacks(sanitizer_function);
|
||||
|
||||
return function_overridden;
|
||||
}
|
||||
|
||||
#endif // SANITIZER_WINDOWS
|
||||
32
lib/tsan/sanitizer_common/sanitizer_win_interception.h
vendored
Normal file
32
lib/tsan/sanitizer_common/sanitizer_win_interception.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
//===-- sanitizer_win_interception.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Windows-specific export surface to provide interception for parts of the
|
||||
// runtime that are always statically linked, both for overriding user-defined
|
||||
// functions as well as registering weak functions that the ASAN runtime should
|
||||
// use over defaults.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_WIN_INTERCEPTION_H
|
||||
#define SANITIZER_WIN_INTERCEPTION_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_WINDOWS
|
||||
|
||||
# include "sanitizer_common.h"
|
||||
# include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
using RegisterWeakFunctionCallback = void (*)();
|
||||
void AddRegisterWeakFunctionCallback(uptr export_address,
|
||||
RegisterWeakFunctionCallback cb);
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_WINDOWS
|
||||
#endif // SANITIZER_WIN_INTERCEPTION_H
|
||||
@@ -1,94 +0,0 @@
|
||||
//===-- sanitizer_win_weak_interception.cpp -------------------------------===//
|
||||
//
|
||||
// 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 module should be included in the sanitizer when it is implemented as a
|
||||
// shared library on Windows (dll), in order to delegate the calls of weak
|
||||
// functions to the implementation in the main executable when a strong
|
||||
// definition is provided.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_WINDOWS && SANITIZER_DYNAMIC
|
||||
#include "sanitizer_win_weak_interception.h"
|
||||
#include "sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_interface_internal.h"
|
||||
#include "sanitizer_win_defs.h"
|
||||
#include "interception/interception.h"
|
||||
|
||||
extern "C" {
|
||||
void *WINAPI GetModuleHandleA(const char *module_name);
|
||||
void abort();
|
||||
}
|
||||
|
||||
namespace __sanitizer {
|
||||
// Try to get a pointer to real_function in the main module and override
|
||||
// dll_function with that pointer. If the function isn't found, nothing changes.
|
||||
int interceptWhenPossible(uptr dll_function, const char *real_function) {
|
||||
uptr real = __interception::InternalGetProcAddress(
|
||||
(void *)GetModuleHandleA(0), real_function);
|
||||
if (real && !__interception::OverrideFunction((uptr)dll_function, real, 0))
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
||||
// Declare weak hooks.
|
||||
extern "C" {
|
||||
void __sanitizer_on_print(const char *str);
|
||||
void __sanitizer_weak_hook_memcmp(uptr called_pc, const void *s1,
|
||||
const void *s2, uptr n, int result);
|
||||
void __sanitizer_weak_hook_strcmp(uptr called_pc, const char *s1,
|
||||
const char *s2, int result);
|
||||
void __sanitizer_weak_hook_strncmp(uptr called_pc, const char *s1,
|
||||
const char *s2, uptr n, int result);
|
||||
void __sanitizer_weak_hook_strstr(uptr called_pc, const char *s1,
|
||||
const char *s2, char *result);
|
||||
}
|
||||
|
||||
// Include Sanitizer Common interface.
|
||||
#define INTERFACE_FUNCTION(Name)
|
||||
#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
|
||||
#include "sanitizer_common_interface.inc"
|
||||
|
||||
#pragma section(".WEAK$A", read)
|
||||
#pragma section(".WEAK$Z", read)
|
||||
|
||||
typedef void (*InterceptCB)();
|
||||
extern "C" {
|
||||
__declspec(allocate(".WEAK$A")) InterceptCB __start_weak_list;
|
||||
__declspec(allocate(".WEAK$Z")) InterceptCB __stop_weak_list;
|
||||
}
|
||||
|
||||
static int weak_intercept_init() {
|
||||
static bool flag = false;
|
||||
// weak_interception_init is expected to be called by only one thread.
|
||||
if (flag) return 0;
|
||||
flag = true;
|
||||
|
||||
for (InterceptCB *it = &__start_weak_list; it < &__stop_weak_list; ++it)
|
||||
if (*it)
|
||||
(*it)();
|
||||
|
||||
// In DLLs, the callbacks are expected to return 0,
|
||||
// otherwise CRT initialization fails.
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma section(".CRT$XIB", long, read)
|
||||
__declspec(allocate(".CRT$XIB")) int (*__weak_intercept_preinit)() =
|
||||
weak_intercept_init;
|
||||
|
||||
static void WINAPI weak_intercept_thread_init(void *mod, unsigned long reason,
|
||||
void *reserved) {
|
||||
if (reason == /*DLL_PROCESS_ATTACH=*/1) weak_intercept_init();
|
||||
}
|
||||
|
||||
#pragma section(".CRT$XLAB", long, read)
|
||||
__declspec(allocate(".CRT$XLAB")) void(WINAPI *__weak_intercept_tls_init)(
|
||||
void *, unsigned long, void *) = weak_intercept_thread_init;
|
||||
|
||||
#endif // SANITIZER_WINDOWS && SANITIZER_DYNAMIC
|
||||
@@ -1,32 +0,0 @@
|
||||
//===-- sanitizer_win_weak_interception.h ---------------------------------===//
|
||||
//
|
||||
// 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 header provide helper macros to delegate calls of weak functions to the
|
||||
// implementation in the main executable when a strong definition is present.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_WIN_WEAK_INTERCEPTION_H
|
||||
#define SANITIZER_WIN_WEAK_INTERCEPTION_H
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
int interceptWhenPossible(uptr dll_function, const char *real_function);
|
||||
}
|
||||
|
||||
// ----------------- Function interception helper macros -------------------- //
|
||||
// Weak functions, could be redefined in the main executable, but that is not
|
||||
// necessary, so we shouldn't die if we can not find a reference.
|
||||
#define INTERCEPT_WEAK(Name) interceptWhenPossible((uptr) Name, #Name);
|
||||
|
||||
#define INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) \
|
||||
static int intercept_##Name() { \
|
||||
return __sanitizer::interceptWhenPossible((__sanitizer::uptr) Name, #Name);\
|
||||
} \
|
||||
__pragma(section(".WEAK$M", long, read)) \
|
||||
__declspec(allocate(".WEAK$M")) int (*__weak_intercept_##Name)() = \
|
||||
intercept_##Name;
|
||||
|
||||
#endif // SANITIZER_WIN_WEAK_INTERCEPTION_H
|
||||
208
lib/tsan/tsan_interceptors_mac.cpp
vendored
208
lib/tsan/tsan_interceptors_mac.cpp
vendored
@@ -14,22 +14,21 @@
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_APPLE
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "tsan_interceptors.h"
|
||||
#include "tsan_interface.h"
|
||||
#include "tsan_interface_ann.h"
|
||||
#include "tsan_spinlock_defs_mac.h"
|
||||
#include "sanitizer_common/sanitizer_addrhashmap.h"
|
||||
# include <errno.h>
|
||||
# include <libkern/OSAtomic.h>
|
||||
# include <objc/objc-sync.h>
|
||||
# include <os/lock.h>
|
||||
# include <sys/ucontext.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <objc/objc-sync.h>
|
||||
#include <os/lock.h>
|
||||
#include <sys/ucontext.h>
|
||||
# include "interception/interception.h"
|
||||
# include "sanitizer_common/sanitizer_addrhashmap.h"
|
||||
# include "tsan_interceptors.h"
|
||||
# include "tsan_interface.h"
|
||||
# include "tsan_interface_ann.h"
|
||||
|
||||
#if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
#include <xpc/xpc.h>
|
||||
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
# if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
# include <xpc/xpc.h>
|
||||
# endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
|
||||
typedef long long_t;
|
||||
|
||||
@@ -49,51 +48,55 @@ static constexpr morder kMacOrderBarrier = mo_acq_rel;
|
||||
static constexpr morder kMacOrderNonBarrier = mo_acq_rel;
|
||||
static constexpr morder kMacFailureOrder = mo_relaxed;
|
||||
|
||||
#define OSATOMIC_INTERCEPTOR(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, x, mo); \
|
||||
}
|
||||
# define OSATOMIC_INTERCEPTOR(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, x, mo); \
|
||||
}
|
||||
|
||||
#define OSATOMIC_INTERCEPTOR_PLUS_X(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, x, mo) + x; \
|
||||
}
|
||||
# define OSATOMIC_INTERCEPTOR_PLUS_X(return_t, t, tsan_t, f, tsan_atomic_f, \
|
||||
mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, x, mo) + x; \
|
||||
}
|
||||
|
||||
#define OSATOMIC_INTERCEPTOR_PLUS_1(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) + 1; \
|
||||
}
|
||||
# define OSATOMIC_INTERCEPTOR_PLUS_1(return_t, t, tsan_t, f, tsan_atomic_f, \
|
||||
mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) + 1; \
|
||||
}
|
||||
|
||||
#define OSATOMIC_INTERCEPTOR_MINUS_1(return_t, t, tsan_t, f, tsan_atomic_f, \
|
||||
mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) - 1; \
|
||||
}
|
||||
# define OSATOMIC_INTERCEPTOR_MINUS_1(return_t, t, tsan_t, f, tsan_atomic_f, \
|
||||
mo) \
|
||||
TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, ptr); \
|
||||
return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) - 1; \
|
||||
}
|
||||
|
||||
#define OSATOMIC_INTERCEPTORS_ARITHMETIC(f, tsan_atomic_f, m) \
|
||||
m(int32_t, int32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m(int32_t, int32_t, a32, f##32##Barrier, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderBarrier) \
|
||||
m(int64_t, int64_t, a64, f##64, __tsan_atomic64_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m(int64_t, int64_t, a64, f##64##Barrier, __tsan_atomic64_##tsan_atomic_f, \
|
||||
kMacOrderBarrier)
|
||||
# define OSATOMIC_INTERCEPTORS_ARITHMETIC(f, tsan_atomic_f, m) \
|
||||
m(int32_t, int32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m(int32_t, int32_t, a32, f##32##Barrier, \
|
||||
__tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier) \
|
||||
m(int64_t, int64_t, a64, f##64, __tsan_atomic64_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m(int64_t, int64_t, a64, f##64##Barrier, \
|
||||
__tsan_atomic64_##tsan_atomic_f, kMacOrderBarrier)
|
||||
|
||||
#define OSATOMIC_INTERCEPTORS_BITWISE(f, tsan_atomic_f, m, m_orig) \
|
||||
m(int32_t, uint32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m(int32_t, uint32_t, a32, f##32##Barrier, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderBarrier) \
|
||||
m_orig(int32_t, uint32_t, a32, f##32##Orig, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m_orig(int32_t, uint32_t, a32, f##32##OrigBarrier, \
|
||||
__tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier)
|
||||
# define OSATOMIC_INTERCEPTORS_BITWISE(f, tsan_atomic_f, m, m_orig) \
|
||||
m(int32_t, uint32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
|
||||
kMacOrderNonBarrier) \
|
||||
m(int32_t, uint32_t, a32, f##32##Barrier, \
|
||||
__tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier) \
|
||||
m_orig(int32_t, uint32_t, a32, f##32##Orig, \
|
||||
__tsan_atomic32_##tsan_atomic_f, kMacOrderNonBarrier) \
|
||||
m_orig(int32_t, uint32_t, a32, f##32##OrigBarrier, \
|
||||
__tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier)
|
||||
|
||||
# pragma clang diagnostic push // OSAtomic* deprecation
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicAdd, fetch_add,
|
||||
OSATOMIC_INTERCEPTOR_PLUS_X)
|
||||
OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicIncrement, fetch_add,
|
||||
@@ -106,23 +109,26 @@ OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicAnd, fetch_and,
|
||||
OSATOMIC_INTERCEPTOR_PLUS_X, OSATOMIC_INTERCEPTOR)
|
||||
OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicXor, fetch_xor,
|
||||
OSATOMIC_INTERCEPTOR_PLUS_X, OSATOMIC_INTERCEPTOR)
|
||||
# pragma clang diagnostic pop // OSAtomic* deprecation
|
||||
|
||||
#define OSATOMIC_INTERCEPTORS_CAS(f, tsan_atomic_f, tsan_t, t) \
|
||||
TSAN_INTERCEPTOR(bool, f, t old_value, t new_value, t volatile *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, old_value, new_value, ptr); \
|
||||
return tsan_atomic_f##_compare_exchange_strong( \
|
||||
(volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
|
||||
kMacOrderNonBarrier, kMacFailureOrder); \
|
||||
} \
|
||||
\
|
||||
TSAN_INTERCEPTOR(bool, f##Barrier, t old_value, t new_value, \
|
||||
t volatile *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f##Barrier, old_value, new_value, ptr); \
|
||||
return tsan_atomic_f##_compare_exchange_strong( \
|
||||
(volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
|
||||
kMacOrderBarrier, kMacFailureOrder); \
|
||||
}
|
||||
# define OSATOMIC_INTERCEPTORS_CAS(f, tsan_atomic_f, tsan_t, t) \
|
||||
TSAN_INTERCEPTOR(bool, f, t old_value, t new_value, t volatile *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, old_value, new_value, ptr); \
|
||||
return tsan_atomic_f##_compare_exchange_strong( \
|
||||
(volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
|
||||
kMacOrderNonBarrier, kMacFailureOrder); \
|
||||
} \
|
||||
\
|
||||
TSAN_INTERCEPTOR(bool, f##Barrier, t old_value, t new_value, \
|
||||
t volatile *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f##Barrier, old_value, new_value, ptr); \
|
||||
return tsan_atomic_f##_compare_exchange_strong( \
|
||||
(volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
|
||||
kMacOrderBarrier, kMacFailureOrder); \
|
||||
}
|
||||
|
||||
# pragma clang diagnostic push // OSAtomicCompareAndSwap* deprecation
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapInt, __tsan_atomic32, a32, int)
|
||||
OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapLong, __tsan_atomic64, a64,
|
||||
long_t)
|
||||
@@ -132,24 +138,28 @@ OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap32, __tsan_atomic32, a32,
|
||||
int32_t)
|
||||
OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap64, __tsan_atomic64, a64,
|
||||
int64_t)
|
||||
# pragma clang diagnostic pop // OSAtomicCompareAndSwap* deprecation
|
||||
|
||||
#define OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, mo) \
|
||||
TSAN_INTERCEPTOR(bool, f, uint32_t n, volatile void *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, n, ptr); \
|
||||
volatile char *byte_ptr = ((volatile char *)ptr) + (n >> 3); \
|
||||
char bit = 0x80u >> (n & 7); \
|
||||
char mask = clear ? ~bit : bit; \
|
||||
char orig_byte = op((volatile a8 *)byte_ptr, mask, mo); \
|
||||
return orig_byte & bit; \
|
||||
}
|
||||
# define OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, mo) \
|
||||
TSAN_INTERCEPTOR(bool, f, uint32_t n, volatile void *ptr) { \
|
||||
SCOPED_TSAN_INTERCEPTOR(f, n, ptr); \
|
||||
volatile char *byte_ptr = ((volatile char *)ptr) + (n >> 3); \
|
||||
char bit = 0x80u >> (n & 7); \
|
||||
char mask = clear ? ~bit : bit; \
|
||||
char orig_byte = op((volatile a8 *)byte_ptr, mask, mo); \
|
||||
return orig_byte & bit; \
|
||||
}
|
||||
|
||||
#define OSATOMIC_INTERCEPTORS_BITOP(f, op, clear) \
|
||||
OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, kMacOrderNonBarrier) \
|
||||
OSATOMIC_INTERCEPTOR_BITOP(f##Barrier, op, clear, kMacOrderBarrier)
|
||||
# define OSATOMIC_INTERCEPTORS_BITOP(f, op, clear) \
|
||||
OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, kMacOrderNonBarrier) \
|
||||
OSATOMIC_INTERCEPTOR_BITOP(f##Barrier, op, clear, kMacOrderBarrier)
|
||||
|
||||
# pragma clang diagnostic push // OSAtomicTestAnd* deprecation
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
OSATOMIC_INTERCEPTORS_BITOP(OSAtomicTestAndSet, __tsan_atomic8_fetch_or, false)
|
||||
OSATOMIC_INTERCEPTORS_BITOP(OSAtomicTestAndClear, __tsan_atomic8_fetch_and,
|
||||
true)
|
||||
# pragma clang diagnostic pop // OSAtomicTestAnd* deprecation
|
||||
|
||||
TSAN_INTERCEPTOR(void, OSAtomicEnqueue, OSQueueHead *list, void *item,
|
||||
size_t offset) {
|
||||
@@ -161,12 +171,13 @@ TSAN_INTERCEPTOR(void, OSAtomicEnqueue, OSQueueHead *list, void *item,
|
||||
TSAN_INTERCEPTOR(void *, OSAtomicDequeue, OSQueueHead *list, size_t offset) {
|
||||
SCOPED_TSAN_INTERCEPTOR(OSAtomicDequeue, list, offset);
|
||||
void *item = REAL(OSAtomicDequeue)(list, offset);
|
||||
if (item) __tsan_acquire(item);
|
||||
if (item)
|
||||
__tsan_acquire(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
// OSAtomicFifoEnqueue and OSAtomicFifoDequeue are only on OS X.
|
||||
#if !SANITIZER_IOS
|
||||
# if !SANITIZER_IOS
|
||||
|
||||
TSAN_INTERCEPTOR(void, OSAtomicFifoEnqueue, OSFifoQueueHead *list, void *item,
|
||||
size_t offset) {
|
||||
@@ -179,11 +190,22 @@ TSAN_INTERCEPTOR(void *, OSAtomicFifoDequeue, OSFifoQueueHead *list,
|
||||
size_t offset) {
|
||||
SCOPED_TSAN_INTERCEPTOR(OSAtomicFifoDequeue, list, offset);
|
||||
void *item = REAL(OSAtomicFifoDequeue)(list, offset);
|
||||
if (item) __tsan_acquire(item);
|
||||
if (item)
|
||||
__tsan_acquire(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
#endif
|
||||
# endif
|
||||
|
||||
// If `OSSPINLOCK_USE_INLINED=1` is set, then SDK headers don't declare these
|
||||
// as functions, but macros that call non-deprecated APIs. Undefine these
|
||||
// macros so they don't interfere with the interceptor machinery.
|
||||
# undef OSSpinLockLock
|
||||
# undef OSSpinLockTry
|
||||
# undef OSSpinLockUnlock
|
||||
|
||||
# pragma clang diagnostic push // OSSpinLock* deprecation
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
TSAN_INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) {
|
||||
CHECK(!cur_thread()->is_dead);
|
||||
@@ -216,6 +238,7 @@ TSAN_INTERCEPTOR(void, OSSpinLockUnlock, volatile OSSpinLock *lock) {
|
||||
Release(thr, pc, (uptr)lock);
|
||||
REAL(OSSpinLockUnlock)(lock);
|
||||
}
|
||||
# pragma clang diagnostic pop // OSSpinLock* deprecation
|
||||
|
||||
TSAN_INTERCEPTOR(void, os_lock_lock, void *lock) {
|
||||
CHECK(!cur_thread()->is_dead);
|
||||
@@ -288,7 +311,7 @@ TSAN_INTERCEPTOR(void, os_unfair_lock_unlock, os_unfair_lock_t lock) {
|
||||
REAL(os_unfair_lock_unlock)(lock);
|
||||
}
|
||||
|
||||
#if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
# if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
|
||||
TSAN_INTERCEPTOR(void, xpc_connection_set_event_handler,
|
||||
xpc_connection_t connection, xpc_handler_t handler) {
|
||||
@@ -342,7 +365,7 @@ TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
|
||||
REAL(xpc_connection_cancel)(connection);
|
||||
}
|
||||
|
||||
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
# endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
|
||||
// Determines whether the Obj-C object pointer is a tagged pointer. Tagged
|
||||
// pointers encode the object data directly in their pointer bits and do not
|
||||
@@ -365,7 +388,7 @@ static uptr GetOrCreateSyncAddress(uptr addr, ThreadState *thr, uptr pc) {
|
||||
Map::Handle h(&Addresses, addr);
|
||||
if (h.created()) {
|
||||
ThreadIgnoreBegin(thr, pc);
|
||||
*h = (uptr) user_alloc(thr, pc, /*size=*/1);
|
||||
*h = (uptr)user_alloc(thr, pc, /*size=*/1);
|
||||
ThreadIgnoreEnd(thr);
|
||||
}
|
||||
return *h;
|
||||
@@ -383,7 +406,8 @@ static uptr SyncAddressForObjCObject(id obj, ThreadState *thr, uptr pc) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, objc_sync_enter, id obj) {
|
||||
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
|
||||
if (!obj) return REAL(objc_sync_enter)(obj);
|
||||
if (!obj)
|
||||
return REAL(objc_sync_enter)(obj);
|
||||
uptr addr = SyncAddressForObjCObject(obj, thr, pc);
|
||||
MutexPreLock(thr, pc, addr, MutexFlagWriteReentrant);
|
||||
int result = REAL(objc_sync_enter)(obj);
|
||||
@@ -394,11 +418,13 @@ TSAN_INTERCEPTOR(int, objc_sync_enter, id obj) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, objc_sync_exit, id obj) {
|
||||
SCOPED_TSAN_INTERCEPTOR(objc_sync_exit, obj);
|
||||
if (!obj) return REAL(objc_sync_exit)(obj);
|
||||
if (!obj)
|
||||
return REAL(objc_sync_exit)(obj);
|
||||
uptr addr = SyncAddressForObjCObject(obj, thr, pc);
|
||||
MutexUnlock(thr, pc, addr);
|
||||
int result = REAL(objc_sync_exit)(obj);
|
||||
if (result != OBJC_SYNC_SUCCESS) MutexInvalidAccess(thr, pc, addr);
|
||||
if (result != OBJC_SYNC_SUCCESS)
|
||||
MutexInvalidAccess(thr, pc, addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -429,7 +455,7 @@ TSAN_INTERCEPTOR(int, swapcontext, ucontext_t *oucp, const ucontext_t *ucp) {
|
||||
|
||||
// On macOS, libc++ is always linked dynamically, so intercepting works the
|
||||
// usual way.
|
||||
#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
|
||||
# define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
|
||||
|
||||
namespace {
|
||||
struct fake_shared_weak_count {
|
||||
|
||||
62
lib/tsan/tsan_interceptors_posix.cpp
vendored
62
lib/tsan/tsan_interceptors_posix.cpp
vendored
@@ -12,14 +12,15 @@
|
||||
// sanitizer_common/sanitizer_common_interceptors.inc
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_allocator_dlsym.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_errno.h"
|
||||
#include "sanitizer_common/sanitizer_glibc_version.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_linux.h"
|
||||
#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
|
||||
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_posix.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "sanitizer_common/sanitizer_tls_get_addr.h"
|
||||
@@ -96,7 +97,7 @@ extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
|
||||
extern "C" int pthread_setspecific(unsigned key, const void *v);
|
||||
DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *)
|
||||
DECLARE_REAL(int, fflush, __sanitizer_FILE *fp)
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, usize size)
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
|
||||
extern "C" int pthread_equal(void *t1, void *t2);
|
||||
extern "C" void *pthread_self();
|
||||
@@ -252,6 +253,13 @@ SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionBegin() {}
|
||||
SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionEnd() {}
|
||||
#endif
|
||||
|
||||
// FIXME: Use for `in_symbolizer()` as well. As-is we can't use
|
||||
// `DlSymAllocator`, because it uses the primary allocator only. Symbolizer
|
||||
// requires support of the secondary allocator for larger blocks.
|
||||
struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
|
||||
static bool UseImpl() { return (ctx && !ctx->initialized); }
|
||||
};
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
static ThreadSignalContext *SigCtx(ThreadState *thr) {
|
||||
@@ -661,6 +669,8 @@ TSAN_INTERCEPTOR(void, _longjmp, uptr *env, int val) {
|
||||
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
|
||||
if (in_symbolizer())
|
||||
return InternalAlloc(size);
|
||||
if (DlsymAlloc::Use())
|
||||
return DlsymAlloc::Allocate(size);
|
||||
void *p = 0;
|
||||
{
|
||||
SCOPED_INTERCEPTOR_RAW(malloc, size);
|
||||
@@ -678,12 +688,14 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
|
||||
return user_memalign(thr, pc, align, sz);
|
||||
}
|
||||
|
||||
TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
|
||||
TSAN_INTERCEPTOR(void *, calloc, uptr n, uptr size) {
|
||||
if (in_symbolizer())
|
||||
return InternalCalloc(size, n);
|
||||
return InternalCalloc(n, size);
|
||||
if (DlsymAlloc::Use())
|
||||
return DlsymAlloc::Callocate(n, size);
|
||||
void *p = 0;
|
||||
{
|
||||
SCOPED_INTERCEPTOR_RAW(calloc, size, n);
|
||||
SCOPED_INTERCEPTOR_RAW(calloc, n, size);
|
||||
p = user_calloc(thr, pc, size, n);
|
||||
}
|
||||
invoke_malloc_hook(p, n * size);
|
||||
@@ -693,6 +705,8 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
|
||||
TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
|
||||
if (in_symbolizer())
|
||||
return InternalRealloc(p, size);
|
||||
if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(p))
|
||||
return DlsymAlloc::Realloc(p, size);
|
||||
if (p)
|
||||
invoke_free_hook(p);
|
||||
{
|
||||
@@ -703,13 +717,13 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
|
||||
return p;
|
||||
}
|
||||
|
||||
TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) {
|
||||
TSAN_INTERCEPTOR(void *, reallocarray, void *p, uptr n, uptr size) {
|
||||
if (in_symbolizer())
|
||||
return InternalReallocArray(p, size, n);
|
||||
return InternalReallocArray(p, n, size);
|
||||
if (p)
|
||||
invoke_free_hook(p);
|
||||
{
|
||||
SCOPED_INTERCEPTOR_RAW(reallocarray, p, size, n);
|
||||
SCOPED_INTERCEPTOR_RAW(reallocarray, p, n, size);
|
||||
p = user_reallocarray(thr, pc, p, size, n);
|
||||
}
|
||||
invoke_malloc_hook(p, size);
|
||||
@@ -717,20 +731,24 @@ TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) {
|
||||
}
|
||||
|
||||
TSAN_INTERCEPTOR(void, free, void *p) {
|
||||
if (p == 0)
|
||||
if (UNLIKELY(!p))
|
||||
return;
|
||||
if (in_symbolizer())
|
||||
return InternalFree(p);
|
||||
if (DlsymAlloc::PointerIsMine(p))
|
||||
return DlsymAlloc::Free(p);
|
||||
invoke_free_hook(p);
|
||||
SCOPED_INTERCEPTOR_RAW(free, p);
|
||||
user_free(thr, pc, p);
|
||||
}
|
||||
|
||||
TSAN_INTERCEPTOR(void, cfree, void *p) {
|
||||
if (p == 0)
|
||||
if (UNLIKELY(!p))
|
||||
return;
|
||||
if (in_symbolizer())
|
||||
return InternalFree(p);
|
||||
if (DlsymAlloc::PointerIsMine(p))
|
||||
return DlsymAlloc::Free(p);
|
||||
invoke_free_hook(p);
|
||||
SCOPED_INTERCEPTOR_RAW(cfree, p);
|
||||
user_free(thr, pc, p);
|
||||
@@ -750,7 +768,7 @@ TSAN_INTERCEPTOR(char *, strcpy, char *dst, const char *src) {
|
||||
return REAL(strcpy)(dst, src);
|
||||
}
|
||||
|
||||
TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) {
|
||||
TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, usize n) {
|
||||
SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n);
|
||||
uptr srclen = internal_strnlen(src, n);
|
||||
MemoryAccessRange(thr, pc, (uptr)dst, n, true);
|
||||
@@ -1097,7 +1115,7 @@ int internal_pthread_create(void *th, void *attr, void *(*callback)(void *),
|
||||
}
|
||||
int internal_pthread_join(void *th, void **ret) {
|
||||
ScopedIgnoreInterceptors ignore;
|
||||
return REAL(pthread_join(th, ret));
|
||||
return REAL(pthread_join)(th, ret);
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
||||
@@ -1662,13 +1680,23 @@ TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
|
||||
#endif
|
||||
|
||||
TSAN_INTERCEPTOR(int, open, const char *name, int oflag, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, oflag);
|
||||
mode_t mode = va_arg(ap, int);
|
||||
va_end(ap);
|
||||
mode_t mode = 0;
|
||||
if (OpenReadsVaArgs(oflag)) {
|
||||
va_list ap;
|
||||
va_start(ap, oflag);
|
||||
mode = va_arg(ap, int);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
SCOPED_TSAN_INTERCEPTOR(open, name, oflag, mode);
|
||||
READ_STRING(thr, pc, name, 0);
|
||||
int fd = REAL(open)(name, oflag, mode);
|
||||
|
||||
int fd;
|
||||
if (OpenReadsVaArgs(oflag))
|
||||
fd = REAL(open)(name, oflag, mode);
|
||||
else
|
||||
fd = REAL(open)(name, oflag);
|
||||
|
||||
if (fd >= 0)
|
||||
FdFileCreate(thr, pc, fd);
|
||||
return fd;
|
||||
|
||||
151
lib/tsan/tsan_interface.h
vendored
151
lib/tsan/tsan_interface.h
vendored
@@ -16,8 +16,8 @@
|
||||
#define TSAN_INTERFACE_H
|
||||
|
||||
#include <sanitizer_common/sanitizer_internal_defs.h>
|
||||
using __sanitizer::uptr;
|
||||
using __sanitizer::tid_t;
|
||||
using __sanitizer::uptr;
|
||||
|
||||
// This header should NOT include any other headers.
|
||||
// All functions in this header are extern "C" and start with __tsan_.
|
||||
@@ -203,17 +203,18 @@ int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id,
|
||||
namespace __tsan {
|
||||
|
||||
// These should match declarations from public tsan_interface_atomic.h header.
|
||||
typedef unsigned char a8;
|
||||
typedef unsigned char a8;
|
||||
typedef unsigned short a16;
|
||||
typedef unsigned int a32;
|
||||
typedef unsigned int a32;
|
||||
typedef unsigned long long a64;
|
||||
#if !SANITIZER_GO && (defined(__SIZEOF_INT128__) \
|
||||
|| (__clang_major__ * 100 + __clang_minor__ >= 302)) && \
|
||||
#if !SANITIZER_GO && \
|
||||
(defined(__SIZEOF_INT128__) || \
|
||||
(__clang_major__ * 100 + __clang_minor__ >= 302)) && \
|
||||
!defined(__mips64) && !defined(__s390x__)
|
||||
__extension__ typedef __int128 a128;
|
||||
# define __TSAN_HAS_INT128 1
|
||||
# define __TSAN_HAS_INT128 1
|
||||
#else
|
||||
# define __TSAN_HAS_INT128 0
|
||||
# define __TSAN_HAS_INT128 0
|
||||
#endif
|
||||
|
||||
// Part of ABI, do not change.
|
||||
@@ -231,180 +232,180 @@ struct ThreadState;
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_load(const volatile a8 *a, morder mo);
|
||||
a8 __tsan_atomic8_load(const volatile a8 *a, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_load(const volatile a16 *a, morder mo);
|
||||
a16 __tsan_atomic16_load(const volatile a16 *a, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_load(const volatile a32 *a, morder mo);
|
||||
a32 __tsan_atomic32_load(const volatile a32 *a, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_load(const volatile a64 *a, morder mo);
|
||||
a64 __tsan_atomic64_load(const volatile a64 *a, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_load(const volatile a128 *a, morder mo);
|
||||
a128 __tsan_atomic128_load(const volatile a128 *a, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo);
|
||||
void __tsan_atomic8_store(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo);
|
||||
void __tsan_atomic16_store(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo);
|
||||
void __tsan_atomic32_store(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo);
|
||||
void __tsan_atomic64_store(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo);
|
||||
void __tsan_atomic128_store(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo);
|
||||
a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo);
|
||||
a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo);
|
||||
a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo);
|
||||
a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, int mo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo);
|
||||
a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, int mo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
|
||||
morder mo, morder fmo);
|
||||
int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v, int mo,
|
||||
int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v, morder mo,
|
||||
morder fmo);
|
||||
int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v, int mo,
|
||||
int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v, morder mo,
|
||||
morder fmo);
|
||||
a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v, int mo,
|
||||
int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v,
|
||||
morder mo, morder fmo);
|
||||
a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v, int mo,
|
||||
int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v,
|
||||
morder mo, morder fmo);
|
||||
a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v, int mo,
|
||||
int fmo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v,
|
||||
morder mo, morder fmo);
|
||||
a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, int mo,
|
||||
int fmo);
|
||||
#if __TSAN_HAS_INT128
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v,
|
||||
morder mo, morder fmo);
|
||||
int mo, int fmo);
|
||||
#endif
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic_thread_fence(morder mo);
|
||||
void __tsan_atomic_thread_fence(int mo);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_atomic_signal_fence(morder mo);
|
||||
void __tsan_atomic_signal_fence(int mo);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a);
|
||||
|
||||
916
lib/tsan/tsan_interface_atomic.cpp
vendored
916
lib/tsan/tsan_interface_atomic.cpp
vendored
File diff suppressed because it is too large
Load Diff
2
lib/tsan/tsan_mman.cpp
vendored
2
lib/tsan/tsan_mman.cpp
vendored
@@ -252,7 +252,7 @@ void *user_reallocarray(ThreadState *thr, uptr pc, void *p, uptr size, uptr n) {
|
||||
if (AllocatorMayReturnNull())
|
||||
return SetErrnoOnNull(nullptr);
|
||||
GET_STACK_TRACE_FATAL(thr, pc);
|
||||
ReportReallocArrayOverflow(size, n, &stack);
|
||||
ReportReallocArrayOverflow(n, size, &stack);
|
||||
}
|
||||
return user_realloc(thr, pc, p, size * n);
|
||||
}
|
||||
|
||||
1
lib/tsan/tsan_platform_linux.cpp
vendored
1
lib/tsan/tsan_platform_linux.cpp
vendored
@@ -418,7 +418,6 @@ void InitializePlatform() {
|
||||
Die();
|
||||
}
|
||||
|
||||
InitTlsSize();
|
||||
#endif // !SANITIZER_GO
|
||||
}
|
||||
|
||||
|
||||
5
lib/tsan/tsan_rtl.cpp
vendored
5
lib/tsan/tsan_rtl.cpp
vendored
@@ -673,7 +673,8 @@ void CheckUnwind() {
|
||||
thr->ignore_reads_and_writes++;
|
||||
atomic_store_relaxed(&thr->in_signal_handler, 0);
|
||||
#endif
|
||||
PrintCurrentStackSlow(StackTrace::GetCurrentPc());
|
||||
PrintCurrentStack(StackTrace::GetCurrentPc(),
|
||||
common_flags()->fast_unwind_on_fatal);
|
||||
}
|
||||
|
||||
bool is_initialized;
|
||||
@@ -806,6 +807,7 @@ int Finalize(ThreadState *thr) {
|
||||
|
||||
#if !SANITIZER_GO
|
||||
void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
|
||||
VReport(2, "BeforeFork tid: %llu\n", GetTid());
|
||||
GlobalProcessorLock();
|
||||
// Detaching from the slot makes OnUserFree skip writing to the shadow.
|
||||
// The slot will be locked so any attempts to use it will deadlock anyway.
|
||||
@@ -847,6 +849,7 @@ static void ForkAfter(ThreadState* thr,
|
||||
SlotAttachAndLock(thr);
|
||||
SlotUnlock(thr);
|
||||
GlobalProcessorUnlock();
|
||||
VReport(2, "AfterFork tid: %llu\n", GetTid());
|
||||
}
|
||||
|
||||
void ForkParentAfter(ThreadState* thr, uptr pc) { ForkAfter(thr, false); }
|
||||
|
||||
2
lib/tsan/tsan_rtl.h
vendored
2
lib/tsan/tsan_rtl.h
vendored
@@ -514,7 +514,7 @@ bool IsExpectedReport(uptr addr, uptr size);
|
||||
StackID CurrentStackId(ThreadState *thr, uptr pc);
|
||||
ReportStack *SymbolizeStackId(StackID stack_id);
|
||||
void PrintCurrentStack(ThreadState *thr, uptr pc);
|
||||
void PrintCurrentStackSlow(uptr pc); // uses libunwind
|
||||
void PrintCurrentStack(uptr pc, bool fast); // may uses libunwind
|
||||
MBlock *JavaHeapBlock(uptr addr, uptr *start);
|
||||
|
||||
void Initialize(ThreadState *thr);
|
||||
|
||||
10
lib/tsan/tsan_rtl_report.cpp
vendored
10
lib/tsan/tsan_rtl_report.cpp
vendored
@@ -828,18 +828,18 @@ void PrintCurrentStack(ThreadState *thr, uptr pc) {
|
||||
PrintStack(SymbolizeStack(trace));
|
||||
}
|
||||
|
||||
// Always inlining PrintCurrentStackSlow, because LocatePcInTrace assumes
|
||||
// Always inlining PrintCurrentStack, because LocatePcInTrace assumes
|
||||
// __sanitizer_print_stack_trace exists in the actual unwinded stack, but
|
||||
// tail-call to PrintCurrentStackSlow breaks this assumption because
|
||||
// tail-call to PrintCurrentStack breaks this assumption because
|
||||
// __sanitizer_print_stack_trace disappears after tail-call.
|
||||
// However, this solution is not reliable enough, please see dvyukov's comment
|
||||
// http://reviews.llvm.org/D19148#406208
|
||||
// Also see PR27280 comment 2 and 3 for breaking examples and analysis.
|
||||
ALWAYS_INLINE USED void PrintCurrentStackSlow(uptr pc) {
|
||||
ALWAYS_INLINE USED void PrintCurrentStack(uptr pc, bool fast) {
|
||||
#if !SANITIZER_GO
|
||||
uptr bp = GET_CURRENT_FRAME();
|
||||
auto *ptrace = New<BufferedStackTrace>();
|
||||
ptrace->Unwind(pc, bp, nullptr, false);
|
||||
ptrace->Unwind(pc, bp, nullptr, fast);
|
||||
|
||||
for (uptr i = 0; i < ptrace->size / 2; i++) {
|
||||
uptr tmp = ptrace->trace_buffer[i];
|
||||
@@ -857,6 +857,6 @@ using namespace __tsan;
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_print_stack_trace() {
|
||||
PrintCurrentStackSlow(StackTrace::GetCurrentPc());
|
||||
PrintCurrentStack(StackTrace::GetCurrentPc(), false);
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
10
lib/tsan/tsan_rtl_thread.cpp
vendored
10
lib/tsan/tsan_rtl_thread.cpp
vendored
@@ -165,14 +165,16 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
|
||||
#endif
|
||||
|
||||
uptr stk_addr = 0;
|
||||
uptr stk_size = 0;
|
||||
uptr stk_end = 0;
|
||||
uptr tls_addr = 0;
|
||||
uptr tls_size = 0;
|
||||
uptr tls_end = 0;
|
||||
#if !SANITIZER_GO
|
||||
if (thread_type != ThreadType::Fiber)
|
||||
GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr,
|
||||
&tls_size);
|
||||
GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_end, &tls_addr,
|
||||
&tls_end);
|
||||
#endif
|
||||
uptr stk_size = stk_end - stk_addr;
|
||||
uptr tls_size = tls_end - tls_addr;
|
||||
thr->stk_addr = stk_addr;
|
||||
thr->stk_size = stk_size;
|
||||
thr->tls_addr = tls_addr;
|
||||
|
||||
45
lib/tsan/tsan_spinlock_defs_mac.h
vendored
45
lib/tsan/tsan_spinlock_defs_mac.h
vendored
@@ -1,45 +0,0 @@
|
||||
//===-- tsan_spinlock_defs_mac.h -------------------------------===//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Mac-specific forward-declared function defintions that may be
|
||||
// deprecated in later versions of the OS.
|
||||
// These are needed for interceptors.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if SANITIZER_APPLE
|
||||
|
||||
#ifndef TSAN_SPINLOCK_DEFS_MAC_H
|
||||
#define TSAN_SPINLOCK_DEFS_MAC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
/*
|
||||
Provides forward declarations related to OSSpinLocks on Darwin. These functions are
|
||||
deprecated on macOS version 10.12 and later,
|
||||
and are no longer included in the system headers.
|
||||
|
||||
However, the symbols are still available on the system, so we provide these forward
|
||||
declarations to prevent compilation errors in tsan_interceptors_mac.cpp, which
|
||||
references these functions when defining TSAN interceptor functions.
|
||||
*/
|
||||
|
||||
typedef int32_t OSSpinLock;
|
||||
|
||||
void OSSpinLockLock(volatile OSSpinLock *__lock);
|
||||
void OSSpinLockUnlock(volatile OSSpinLock *__lock);
|
||||
bool OSSpinLockTry(volatile OSSpinLock *__lock);
|
||||
|
||||
}
|
||||
|
||||
#endif //TSAN_SPINLOCK_DEFS_MAC_H
|
||||
#endif // SANITIZER_APPLE
|
||||
@@ -410,9 +410,6 @@ const sanitizer_common_sources = [_][]const u8{
|
||||
"sanitizer_allocator.cpp",
|
||||
"sanitizer_chained_origin_depot.cpp",
|
||||
"sanitizer_common.cpp",
|
||||
"sanitizer_coverage_win_dll_thunk.cpp",
|
||||
"sanitizer_coverage_win_dynamic_runtime_thunk.cpp",
|
||||
"sanitizer_coverage_win_weak_interception.cpp",
|
||||
"sanitizer_deadlock_detector1.cpp",
|
||||
"sanitizer_deadlock_detector2.cpp",
|
||||
"sanitizer_errno.cpp",
|
||||
@@ -452,9 +449,7 @@ const sanitizer_common_sources = [_][]const u8{
|
||||
"sanitizer_tls_get_addr.cpp",
|
||||
"sanitizer_type_traits.cpp",
|
||||
"sanitizer_win.cpp",
|
||||
"sanitizer_win_dll_thunk.cpp",
|
||||
"sanitizer_win_dynamic_runtime_thunk.cpp",
|
||||
"sanitizer_win_weak_interception.cpp",
|
||||
"sanitizer_win_interception.cpp",
|
||||
};
|
||||
|
||||
const sanitizer_nolibc_sources = [_][]const u8{
|
||||
@@ -490,6 +485,7 @@ const sanitizer_symbolizer_sources = [_][]const u8{
|
||||
"sanitizer_symbolizer_report.cpp",
|
||||
"sanitizer_symbolizer_report_fuchsia.cpp",
|
||||
"sanitizer_symbolizer_win.cpp",
|
||||
"sanitizer_thread_history.cpp",
|
||||
"sanitizer_unwind_linux_libcdep.cpp",
|
||||
"sanitizer_unwind_fuchsia.cpp",
|
||||
"sanitizer_unwind_win.cpp",
|
||||
|
||||
Reference in New Issue
Block a user