mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
tsan: Update to LLVM 19.1.0.
This commit is contained in:
parent
7f6b7c5608
commit
a40cdad18c
293
lib/tsan/builtins/assembly.h
vendored
Normal file
293
lib/tsan/builtins/assembly.h
vendored
Normal file
@ -0,0 +1,293 @@
|
||||
//===-- assembly.h - compiler-rt assembler support macros -----------------===//
|
||||
//
|
||||
// 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 macros for use in compiler-rt assembler source.
|
||||
// This file is not part of the interface of this library.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef COMPILERRT_ASSEMBLY_H
|
||||
#define COMPILERRT_ASSEMBLY_H
|
||||
|
||||
#if defined(__linux__) && defined(__CET__)
|
||||
#if __has_include(<cet.h>)
|
||||
#include <cet.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
#define SEPARATOR %%
|
||||
#else
|
||||
#define SEPARATOR ;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define HIDDEN(name) .private_extern name
|
||||
#define LOCAL_LABEL(name) L_##name
|
||||
// tell linker it can break up file at label boundaries
|
||||
#define FILE_LEVEL_DIRECTIVE .subsections_via_symbols
|
||||
#define SYMBOL_IS_FUNC(name)
|
||||
#define CONST_SECTION .const
|
||||
|
||||
#define NO_EXEC_STACK_DIRECTIVE
|
||||
|
||||
#elif defined(__ELF__)
|
||||
|
||||
#define HIDDEN(name) .hidden name
|
||||
#define LOCAL_LABEL(name) .L_##name
|
||||
#define FILE_LEVEL_DIRECTIVE
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#define SYMBOL_IS_FUNC(name) .type name,%function
|
||||
#else
|
||||
#define SYMBOL_IS_FUNC(name) .type name,@function
|
||||
#endif
|
||||
#define CONST_SECTION .section .rodata
|
||||
|
||||
#if defined(__GNU__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
|
||||
defined(__linux__)
|
||||
#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits
|
||||
#else
|
||||
#define NO_EXEC_STACK_DIRECTIVE
|
||||
#endif
|
||||
|
||||
#else // !__APPLE__ && !__ELF__
|
||||
|
||||
#define HIDDEN(name)
|
||||
#define LOCAL_LABEL(name) .L ## name
|
||||
#define FILE_LEVEL_DIRECTIVE
|
||||
#define SYMBOL_IS_FUNC(name) \
|
||||
.def name SEPARATOR \
|
||||
.scl 2 SEPARATOR \
|
||||
.type 32 SEPARATOR \
|
||||
.endef
|
||||
#define CONST_SECTION .section .rdata,"rd"
|
||||
|
||||
#define NO_EXEC_STACK_DIRECTIVE
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#define FUNC_ALIGN \
|
||||
.text SEPARATOR \
|
||||
.balign 16 SEPARATOR
|
||||
#else
|
||||
#define FUNC_ALIGN
|
||||
#endif
|
||||
|
||||
// BTI and PAC gnu property note
|
||||
#define NT_GNU_PROPERTY_TYPE_0 5
|
||||
#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000
|
||||
#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI 1
|
||||
#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC 2
|
||||
|
||||
#if defined(__ARM_FEATURE_BTI_DEFAULT)
|
||||
#define BTI_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_BTI
|
||||
#else
|
||||
#define BTI_FLAG 0
|
||||
#endif
|
||||
|
||||
#if __ARM_FEATURE_PAC_DEFAULT & 3
|
||||
#define PAC_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_PAC
|
||||
#else
|
||||
#define PAC_FLAG 0
|
||||
#endif
|
||||
|
||||
#define GNU_PROPERTY(type, value) \
|
||||
.pushsection .note.gnu.property, "a" SEPARATOR \
|
||||
.p2align 3 SEPARATOR \
|
||||
.word 4 SEPARATOR \
|
||||
.word 16 SEPARATOR \
|
||||
.word NT_GNU_PROPERTY_TYPE_0 SEPARATOR \
|
||||
.asciz "GNU" SEPARATOR \
|
||||
.word type SEPARATOR \
|
||||
.word 4 SEPARATOR \
|
||||
.word value SEPARATOR \
|
||||
.word 0 SEPARATOR \
|
||||
.popsection
|
||||
|
||||
#if BTI_FLAG != 0
|
||||
#define BTI_C hint #34
|
||||
#define BTI_J hint #36
|
||||
#else
|
||||
#define BTI_C
|
||||
#define BTI_J
|
||||
#endif
|
||||
|
||||
#if (BTI_FLAG | PAC_FLAG) != 0
|
||||
#define GNU_PROPERTY_BTI_PAC \
|
||||
GNU_PROPERTY(GNU_PROPERTY_AARCH64_FEATURE_1_AND, BTI_FLAG | PAC_FLAG)
|
||||
#else
|
||||
#define GNU_PROPERTY_BTI_PAC
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__GCC_HAVE_DWARF2_CFI_ASM)
|
||||
#define CFI_START .cfi_startproc
|
||||
#define CFI_END .cfi_endproc
|
||||
#else
|
||||
#define CFI_START
|
||||
#define CFI_END
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
// Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros:
|
||||
// - for '-mthumb -march=armv6' compiler defines '__thumb__'
|
||||
// - for '-mthumb -march=armv7' compiler defines '__thumb__' and '__thumb2__'
|
||||
#if defined(__thumb2__) || defined(__thumb__)
|
||||
#define DEFINE_CODE_STATE .thumb SEPARATOR
|
||||
#define DECLARE_FUNC_ENCODING .thumb_func SEPARATOR
|
||||
#if defined(__thumb2__)
|
||||
#define USE_THUMB_2
|
||||
#define IT(cond) it cond
|
||||
#define ITT(cond) itt cond
|
||||
#define ITE(cond) ite cond
|
||||
#else
|
||||
#define USE_THUMB_1
|
||||
#define IT(cond)
|
||||
#define ITT(cond)
|
||||
#define ITE(cond)
|
||||
#endif // defined(__thumb__2)
|
||||
#else // !defined(__thumb2__) && !defined(__thumb__)
|
||||
#define DEFINE_CODE_STATE .arm SEPARATOR
|
||||
#define DECLARE_FUNC_ENCODING
|
||||
#define IT(cond)
|
||||
#define ITT(cond)
|
||||
#define ITE(cond)
|
||||
#endif
|
||||
|
||||
#if defined(USE_THUMB_1) && defined(USE_THUMB_2)
|
||||
#error "USE_THUMB_1 and USE_THUMB_2 can't be defined together."
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5
|
||||
#define ARM_HAS_BX
|
||||
#endif
|
||||
#if !defined(__ARM_FEATURE_CLZ) && !defined(USE_THUMB_1) && \
|
||||
(__ARM_ARCH >= 6 || (__ARM_ARCH == 5 && !defined(__ARM_ARCH_5__)))
|
||||
#define __ARM_FEATURE_CLZ
|
||||
#endif
|
||||
|
||||
#ifdef ARM_HAS_BX
|
||||
#define JMP(r) bx r
|
||||
#define JMPc(r, c) bx##c r
|
||||
#else
|
||||
#define JMP(r) mov pc, r
|
||||
#define JMPc(r, c) mov##c pc, r
|
||||
#endif
|
||||
|
||||
// pop {pc} can't switch Thumb mode on ARMv4T
|
||||
#if __ARM_ARCH >= 5
|
||||
#define POP_PC() pop {pc}
|
||||
#else
|
||||
#define POP_PC() \
|
||||
pop {ip}; \
|
||||
JMP(ip)
|
||||
#endif
|
||||
|
||||
#if defined(USE_THUMB_2)
|
||||
#define WIDE(op) op.w
|
||||
#else
|
||||
#define WIDE(op) op
|
||||
#endif
|
||||
#else // !defined(__arm)
|
||||
#define DECLARE_FUNC_ENCODING
|
||||
#define DEFINE_CODE_STATE
|
||||
#endif
|
||||
|
||||
#define GLUE2_(a, b) a##b
|
||||
#define GLUE(a, b) GLUE2_(a, b)
|
||||
#define GLUE2(a, b) GLUE2_(a, b)
|
||||
#define GLUE3_(a, b, c) a##b##c
|
||||
#define GLUE3(a, b, c) GLUE3_(a, b, c)
|
||||
#define GLUE4_(a, b, c, d) a##b##c##d
|
||||
#define GLUE4(a, b, c, d) GLUE4_(a, b, c, d)
|
||||
|
||||
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)
|
||||
|
||||
#ifdef VISIBILITY_HIDDEN
|
||||
#define DECLARE_SYMBOL_VISIBILITY(name) \
|
||||
HIDDEN(SYMBOL_NAME(name)) SEPARATOR
|
||||
#define DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) \
|
||||
HIDDEN(name) SEPARATOR
|
||||
#else
|
||||
#define DECLARE_SYMBOL_VISIBILITY(name)
|
||||
#define DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name)
|
||||
#endif
|
||||
|
||||
#define DEFINE_COMPILERRT_FUNCTION(name) \
|
||||
DEFINE_CODE_STATE \
|
||||
FILE_LEVEL_DIRECTIVE SEPARATOR \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
DECLARE_SYMBOL_VISIBILITY(name) \
|
||||
DECLARE_FUNC_ENCODING \
|
||||
SYMBOL_NAME(name):
|
||||
|
||||
#define DEFINE_COMPILERRT_THUMB_FUNCTION(name) \
|
||||
DEFINE_CODE_STATE \
|
||||
FILE_LEVEL_DIRECTIVE SEPARATOR \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
DECLARE_SYMBOL_VISIBILITY(name) SEPARATOR \
|
||||
.thumb_func SEPARATOR \
|
||||
SYMBOL_NAME(name):
|
||||
|
||||
#define DEFINE_COMPILERRT_PRIVATE_FUNCTION(name) \
|
||||
DEFINE_CODE_STATE \
|
||||
FILE_LEVEL_DIRECTIVE SEPARATOR \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
HIDDEN(SYMBOL_NAME(name)) SEPARATOR \
|
||||
DECLARE_FUNC_ENCODING \
|
||||
SYMBOL_NAME(name):
|
||||
|
||||
#define DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(name) \
|
||||
DEFINE_CODE_STATE \
|
||||
.globl name SEPARATOR \
|
||||
SYMBOL_IS_FUNC(name) SEPARATOR \
|
||||
HIDDEN(name) SEPARATOR \
|
||||
DECLARE_FUNC_ENCODING \
|
||||
name:
|
||||
|
||||
#define DEFINE_COMPILERRT_OUTLINE_FUNCTION_UNMANGLED(name) \
|
||||
DEFINE_CODE_STATE \
|
||||
FUNC_ALIGN \
|
||||
.globl name SEPARATOR \
|
||||
SYMBOL_IS_FUNC(name) SEPARATOR \
|
||||
DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) SEPARATOR \
|
||||
DECLARE_FUNC_ENCODING \
|
||||
name: \
|
||||
SEPARATOR CFI_START \
|
||||
SEPARATOR BTI_C
|
||||
|
||||
#define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
DECLARE_SYMBOL_VISIBILITY(name) SEPARATOR \
|
||||
.set SYMBOL_NAME(name), SYMBOL_NAME(target) SEPARATOR
|
||||
|
||||
#if defined(__ARM_EABI__)
|
||||
#define DEFINE_AEABI_FUNCTION_ALIAS(aeabi_name, name) \
|
||||
DEFINE_COMPILERRT_FUNCTION_ALIAS(aeabi_name, name)
|
||||
#else
|
||||
#define DEFINE_AEABI_FUNCTION_ALIAS(aeabi_name, name)
|
||||
#endif
|
||||
|
||||
#ifdef __ELF__
|
||||
#define END_COMPILERRT_FUNCTION(name) \
|
||||
.size SYMBOL_NAME(name), . - SYMBOL_NAME(name)
|
||||
#define END_COMPILERRT_OUTLINE_FUNCTION(name) \
|
||||
CFI_END SEPARATOR \
|
||||
.size SYMBOL_NAME(name), . - SYMBOL_NAME(name)
|
||||
#else
|
||||
#define END_COMPILERRT_FUNCTION(name)
|
||||
#define END_COMPILERRT_OUTLINE_FUNCTION(name) \
|
||||
CFI_END
|
||||
#endif
|
||||
|
||||
#endif // COMPILERRT_ASSEMBLY_H
|
||||
29
lib/tsan/interception/interception.h
vendored
29
lib/tsan/interception/interception.h
vendored
@ -185,6 +185,11 @@ const interpose_substitution substitution_##func_name[] \
|
||||
# else
|
||||
# define __ASM_WEAK_WRAPPER(func) ".weak " #func "\n"
|
||||
# endif // SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
# if defined(__arm__) || defined(__aarch64__)
|
||||
# define ASM_TYPE_FUNCTION_STR "%function"
|
||||
# else
|
||||
# define ASM_TYPE_FUNCTION_STR "@function"
|
||||
# endif
|
||||
// Keep trampoline implementation in sync with sanitizer_common/sanitizer_asm.h
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__); \
|
||||
@ -196,12 +201,14 @@ const interpose_substitution substitution_##func_name[] \
|
||||
__ASM_WEAK_WRAPPER(func) \
|
||||
".set " #func ", " SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n" \
|
||||
".globl " SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n" \
|
||||
".type " SANITIZER_STRINGIFY(TRAMPOLINE(func)) ", %function\n" \
|
||||
".type " SANITIZER_STRINGIFY(TRAMPOLINE(func)) ", " \
|
||||
ASM_TYPE_FUNCTION_STR "\n" \
|
||||
SANITIZER_STRINGIFY(TRAMPOLINE(func)) ":\n" \
|
||||
SANITIZER_STRINGIFY(CFI_STARTPROC) "\n" \
|
||||
SANITIZER_STRINGIFY(ASM_TAIL_CALL) " __interceptor_" \
|
||||
SANITIZER_STRINGIFY(ASM_PREEMPTIBLE_SYM(func)) "\n" \
|
||||
SANITIZER_STRINGIFY(CFI_ENDPROC) "\n" \
|
||||
C_ASM_STARTPROC "\n" \
|
||||
C_ASM_TAIL_CALL(SANITIZER_STRINGIFY(TRAMPOLINE(func)), \
|
||||
"__interceptor_" \
|
||||
SANITIZER_STRINGIFY(ASM_PREEMPTIBLE_SYM(func))) "\n" \
|
||||
C_ASM_ENDPROC "\n" \
|
||||
".size " SANITIZER_STRINGIFY(TRAMPOLINE(func)) ", " \
|
||||
".-" SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n" \
|
||||
);
|
||||
@ -341,6 +348,18 @@ 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.
|
||||
// Define a function, if called, will cause a linker error (undefined _DYNAMIC).
|
||||
// However, -static-pie (which is not common) cannot be detected at link time.
|
||||
extern uptr kDynamic[] asm("_DYNAMIC");
|
||||
inline void DoesNotSupportStaticLinking() {
|
||||
[[maybe_unused]] volatile auto x = &kDynamic;
|
||||
}
|
||||
#else
|
||||
inline void DoesNotSupportStaticLinking() {}
|
||||
#endif
|
||||
} // namespace __interception
|
||||
|
||||
#define INCLUDED_FROM_INTERCEPTION_LIB
|
||||
|
||||
10
lib/tsan/interception/interception_linux.h
vendored
10
lib/tsan/interception/interception_linux.h
vendored
@ -28,11 +28,13 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
|
||||
uptr func, uptr trampoline);
|
||||
} // namespace __interception
|
||||
|
||||
// Cast func to type of REAL(func) before casting to uptr in case it is an
|
||||
// overloaded function, which is the case for some glibc functions when
|
||||
// _FORTIFY_SOURCE is used. This disambiguates which overload to use.
|
||||
#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
|
||||
::__interception::InterceptFunction( \
|
||||
#func, \
|
||||
(::__interception::uptr *)&REAL(func), \
|
||||
(::__interception::uptr)&(func), \
|
||||
#func, (::__interception::uptr *)&REAL(func), \
|
||||
(::__interception::uptr)(decltype(REAL(func)))&(func), \
|
||||
(::__interception::uptr) &TRAMPOLINE(func))
|
||||
|
||||
// dlvsym is a GNU extension supported by some other platforms.
|
||||
@ -41,7 +43,7 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
|
||||
::__interception::InterceptFunction( \
|
||||
#func, symver, \
|
||||
(::__interception::uptr *)&REAL(func), \
|
||||
(::__interception::uptr)&(func), \
|
||||
(::__interception::uptr)(decltype(REAL(func)))&(func), \
|
||||
(::__interception::uptr)&TRAMPOLINE(func))
|
||||
#else
|
||||
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
|
||||
|
||||
53
lib/tsan/interception/interception_win.cpp
vendored
53
lib/tsan/interception/interception_win.cpp
vendored
@ -1,4 +1,4 @@
|
||||
//===-- interception_linux.cpp ----------------------------------*- C++ -*-===//
|
||||
//===-- interception_win.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.
|
||||
@ -339,7 +339,7 @@ struct TrampolineMemoryRegion {
|
||||
uptr max_size;
|
||||
};
|
||||
|
||||
UNUSED static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig
|
||||
UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig
|
||||
static const int kMaxTrampolineRegion = 1024;
|
||||
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
|
||||
|
||||
@ -431,7 +431,8 @@ static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
|
||||
// The following prologues cannot be patched because of the short jump
|
||||
// jumping to the patching region.
|
||||
|
||||
#if SANITIZER_WINDOWS64
|
||||
// Short jump patterns below are only for x86_64.
|
||||
# if SANITIZER_WINDOWS_x64
|
||||
// ntdll!wcslen in Win11
|
||||
// 488bc1 mov rax,rcx
|
||||
// 0fb710 movzx edx,word ptr [rax]
|
||||
@ -457,7 +458,12 @@ static const u8 kPrologueWithShortJump2[] = {
|
||||
|
||||
// Returns 0 on error.
|
||||
static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
#if SANITIZER_WINDOWS64
|
||||
#if SANITIZER_ARM64
|
||||
// An ARM64 instruction is 4 bytes long.
|
||||
return 4;
|
||||
#endif
|
||||
|
||||
# if SANITIZER_WINDOWS_x64
|
||||
if (memcmp((u8*)address, kPrologueWithShortJump1,
|
||||
sizeof(kPrologueWithShortJump1)) == 0 ||
|
||||
memcmp((u8*)address, kPrologueWithShortJump2,
|
||||
@ -473,6 +479,8 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
|
||||
switch (*(u8*)address) {
|
||||
case 0x90: // 90 : nop
|
||||
case 0xC3: // C3 : ret (for small/empty function interception
|
||||
case 0xCC: // CC : int 3 i.e. registering weak functions)
|
||||
return 1;
|
||||
|
||||
case 0x50: // push eax / rax
|
||||
@ -496,7 +504,6 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
// Cannot overwrite control-instruction. Return 0 to indicate failure.
|
||||
case 0xE9: // E9 XX XX XX XX : jmp <label>
|
||||
case 0xE8: // E8 XX XX XX XX : call <func>
|
||||
case 0xC3: // C3 : ret
|
||||
case 0xEB: // EB XX : jmp XX (short jump)
|
||||
case 0x70: // 7Y YY : jy XX (short conditional jump)
|
||||
case 0x71:
|
||||
@ -539,7 +546,12 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
#if SANITIZER_WINDOWS64
|
||||
switch (0x000000FF & *(u32 *)address) {
|
||||
case 0xc2: // C2 XX XX : ret XX (needed for registering weak functions)
|
||||
return 3;
|
||||
}
|
||||
|
||||
# if SANITIZER_WINDOWS_x64
|
||||
switch (*(u8*)address) {
|
||||
case 0xA1: // A1 XX XX XX XX XX XX XX XX :
|
||||
// movabs eax, dword ptr ds:[XXXXXXXX]
|
||||
@ -572,6 +584,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0x018a: // mov al, byte ptr [rcx]
|
||||
return 2;
|
||||
|
||||
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;
|
||||
@ -598,6 +611,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
case 0xc18b4c: // 4C 8B C1 : mov r8, rcx
|
||||
case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
|
||||
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
|
||||
@ -617,9 +631,11 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
|
||||
|
||||
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 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]
|
||||
// Instructions having offset relative to 'rip' need offset adjustment.
|
||||
if (rel_offset)
|
||||
*rel_offset = 3;
|
||||
@ -724,13 +740,19 @@ static bool CopyInstructions(uptr to, uptr from, size_t size) {
|
||||
_memcpy((void *)(to + cursor), (void *)(from + cursor),
|
||||
(size_t)instruction_size);
|
||||
if (rel_offset) {
|
||||
uptr delta = to - from;
|
||||
uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta;
|
||||
# if SANITIZER_WINDOWS64
|
||||
if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU)
|
||||
// we want to make sure that the new relative offset still fits in 32-bits
|
||||
// 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)
|
||||
return false;
|
||||
# else
|
||||
// on 32-bit, the relative offset will always be correct
|
||||
s32 delta = to - from;
|
||||
s32 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
|
||||
# endif
|
||||
*(u32*)(to + cursor + rel_offset) = relocated_offset;
|
||||
*(s32 *)(to + cursor + rel_offset) = relocated_offset;
|
||||
}
|
||||
cursor += instruction_size;
|
||||
}
|
||||
@ -933,6 +955,11 @@ bool OverrideFunction(
|
||||
static void **InterestingDLLsAvailable() {
|
||||
static const char *InterestingDLLs[] = {
|
||||
"kernel32.dll",
|
||||
"msvcr100d.dll", // VS2010
|
||||
"msvcr110d.dll", // VS2012
|
||||
"msvcr120d.dll", // VS2013
|
||||
"vcruntime140d.dll", // VS2015
|
||||
"ucrtbased.dll", // Universal CRT
|
||||
"msvcr100.dll", // VS2010
|
||||
"msvcr110.dll", // VS2012
|
||||
"msvcr120.dll", // VS2013
|
||||
@ -944,7 +971,9 @@ static void **InterestingDLLsAvailable() {
|
||||
# endif
|
||||
// NTDLL should go last as it exports some functions that we should
|
||||
// override in the CRT [presumably only used internally].
|
||||
"ntdll.dll", NULL};
|
||||
"ntdll.dll",
|
||||
NULL
|
||||
};
|
||||
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
|
||||
if (!result[0]) {
|
||||
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
|
||||
|
||||
@ -25,7 +25,7 @@ namespace __sanitizer {
|
||||
const char *PrimaryAllocatorName = "SizeClassAllocator";
|
||||
const char *SecondaryAllocatorName = "LargeMmapAllocator";
|
||||
|
||||
static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
|
||||
alignas(64) static char internal_alloc_placeholder[sizeof(InternalAllocator)];
|
||||
static atomic_uint8_t internal_allocator_initialized;
|
||||
static StaticSpinMutex internal_alloc_init_mu;
|
||||
|
||||
@ -138,14 +138,20 @@ void InternalAllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
|
||||
|
||||
// LowLevelAllocator
|
||||
constexpr uptr kLowLevelAllocatorDefaultAlignment = 8;
|
||||
constexpr uptr kMinNumPagesRounded = 16;
|
||||
constexpr uptr kMinRoundedSize = 65536;
|
||||
static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment;
|
||||
static LowLevelAllocateCallback low_level_alloc_callback;
|
||||
|
||||
static LowLevelAllocator Alloc;
|
||||
LowLevelAllocator &GetGlobalLowLevelAllocator() { return Alloc; }
|
||||
|
||||
void *LowLevelAllocator::Allocate(uptr size) {
|
||||
// Align allocation size.
|
||||
size = RoundUpTo(size, low_level_alloc_min_alignment);
|
||||
if (allocated_end_ - allocated_current_ < (sptr)size) {
|
||||
uptr size_to_allocate = RoundUpTo(size, GetPageSizeCached());
|
||||
uptr size_to_allocate = RoundUpTo(
|
||||
size, Min(GetPageSizeCached() * kMinNumPagesRounded, kMinRoundedSize));
|
||||
allocated_current_ = (char *)MmapOrDie(size_to_allocate, __func__);
|
||||
allocated_end_ = allocated_current_ + size_to_allocate;
|
||||
if (low_level_alloc_callback) {
|
||||
|
||||
@ -40,6 +40,8 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_malloc_hook(void *ptr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_free_hook(void *ptr);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int
|
||||
__sanitizer_ignore_free_hook(void *ptr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_purge_allocator();
|
||||
|
||||
@ -278,7 +278,7 @@ class SizeClassAllocator32 {
|
||||
static const uptr kRegionSize = 1 << kRegionSizeLog;
|
||||
static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize;
|
||||
|
||||
struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo {
|
||||
struct alignas(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo {
|
||||
StaticSpinMutex mutex;
|
||||
IntrusiveList<TransferBatch> free_list;
|
||||
u32 rand_state;
|
||||
|
||||
@ -316,13 +316,13 @@ class SizeClassAllocator64 {
|
||||
Printf(
|
||||
"%s %02zd (%6zd): mapped: %6zdK allocs: %7zd frees: %7zd inuse: %6zd "
|
||||
"num_freed_chunks %7zd avail: %6zd rss: %6zdK releases: %6zd "
|
||||
"last released: %6lldK region: 0x%zx\n",
|
||||
"last released: %6lldK region: %p\n",
|
||||
region->exhausted ? "F" : " ", class_id, ClassIdToSize(class_id),
|
||||
region->mapped_user >> 10, region->stats.n_allocated,
|
||||
region->stats.n_freed, in_use, region->num_freed_chunks, avail_chunks,
|
||||
rss >> 10, region->rtoi.num_releases,
|
||||
region->rtoi.last_released_bytes >> 10,
|
||||
SpaceBeg() + kRegionSize * class_id);
|
||||
(void *)(SpaceBeg() + kRegionSize * class_id));
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
@ -636,15 +636,17 @@ class SizeClassAllocator64 {
|
||||
}
|
||||
uptr SpaceEnd() const { return SpaceBeg() + kSpaceSize; }
|
||||
// kRegionSize should be able to satisfy the largest size class.
|
||||
static_assert(kRegionSize >= SizeClassMap::kMaxSize);
|
||||
static_assert(kRegionSize >= SizeClassMap::kMaxSize,
|
||||
"Region size exceed largest size");
|
||||
// kRegionSize must be <= 2^36, see CompactPtrT.
|
||||
COMPILER_CHECK((kRegionSize) <= (1ULL << (SANITIZER_WORDSIZE / 2 + 4)));
|
||||
COMPILER_CHECK((kRegionSize) <=
|
||||
(1ULL << (sizeof(CompactPtrT) * 8 + kCompactPtrScale)));
|
||||
// Call mmap for user memory with at least this size.
|
||||
static const uptr kUserMapSize = 1 << 16;
|
||||
static const uptr kUserMapSize = 1 << 18;
|
||||
// Call mmap for metadata memory with at least this size.
|
||||
static const uptr kMetaMapSize = 1 << 16;
|
||||
// Call mmap for free array memory with at least this size.
|
||||
static const uptr kFreeArrayMapSize = 1 << 16;
|
||||
static const uptr kFreeArrayMapSize = 1 << 18;
|
||||
|
||||
atomic_sint32_t release_to_os_interval_ms_;
|
||||
|
||||
@ -665,7 +667,7 @@ class SizeClassAllocator64 {
|
||||
u64 last_released_bytes;
|
||||
};
|
||||
|
||||
struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) RegionInfo {
|
||||
struct alignas(SANITIZER_CACHE_LINE_SIZE) RegionInfo {
|
||||
Mutex mutex;
|
||||
uptr num_freed_chunks; // Number of elements in the freearray.
|
||||
uptr mapped_free_array; // Bytes mapped for freearray.
|
||||
|
||||
41
lib/tsan/sanitizer_common/sanitizer_asm.h
vendored
41
lib/tsan/sanitizer_common/sanitizer_asm.h
vendored
@ -42,6 +42,16 @@
|
||||
# define CFI_RESTORE(reg)
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_FEATURE_BTI_DEFAULT)
|
||||
# define ASM_STARTPROC CFI_STARTPROC; hint #34
|
||||
# define C_ASM_STARTPROC SANITIZER_STRINGIFY(CFI_STARTPROC) "\nhint #34"
|
||||
#else
|
||||
# define ASM_STARTPROC CFI_STARTPROC
|
||||
# define C_ASM_STARTPROC SANITIZER_STRINGIFY(CFI_STARTPROC)
|
||||
#endif
|
||||
#define ASM_ENDPROC CFI_ENDPROC
|
||||
#define C_ASM_ENDPROC SANITIZER_STRINGIFY(CFI_ENDPROC)
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(__sparc__)
|
||||
# define ASM_TAIL_CALL jmp
|
||||
#elif defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
|
||||
@ -53,6 +63,29 @@
|
||||
# define ASM_TAIL_CALL tail
|
||||
#endif
|
||||
|
||||
// Currently, almost all of the shared libraries rely on the value of
|
||||
// $t9 to get the address of current function, instead of PCREL, even
|
||||
// on MIPSr6. To be compatiable with them, we have to set $t9 properly.
|
||||
// MIPS uses GOT to get the address of preemptible functions.
|
||||
#if defined(__mips64)
|
||||
# define C_ASM_TAIL_CALL(t_func, i_func) \
|
||||
"lui $t8, %hi(%neg(%gp_rel(" t_func ")))\n" \
|
||||
"daddu $t8, $t8, $t9\n" \
|
||||
"daddiu $t8, $t8, %lo(%neg(%gp_rel(" t_func ")))\n" \
|
||||
"ld $t9, %got_disp(" i_func ")($t8)\n" \
|
||||
"jr $t9\n"
|
||||
#elif defined(__mips__)
|
||||
# define C_ASM_TAIL_CALL(t_func, i_func) \
|
||||
".set noreorder\n" \
|
||||
".cpload $t9\n" \
|
||||
".set reorder\n" \
|
||||
"lw $t9, %got(" i_func ")($gp)\n" \
|
||||
"jr $t9\n"
|
||||
#elif defined(ASM_TAIL_CALL)
|
||||
# define C_ASM_TAIL_CALL(t_func, i_func) \
|
||||
SANITIZER_STRINGIFY(ASM_TAIL_CALL) " " i_func
|
||||
#endif
|
||||
|
||||
#if defined(__ELF__) && defined(__x86_64__) || defined(__i386__) || \
|
||||
defined(__riscv)
|
||||
# define ASM_PREEMPTIBLE_SYM(sym) sym@plt
|
||||
@ -62,7 +95,11 @@
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
# define ASM_HIDDEN(symbol) .hidden symbol
|
||||
# if defined(__arm__) || defined(__aarch64__)
|
||||
# define ASM_TYPE_FUNCTION(symbol) .type symbol, %function
|
||||
# else
|
||||
# define ASM_TYPE_FUNCTION(symbol) .type symbol, @function
|
||||
# endif
|
||||
# define ASM_SIZE(symbol) .size symbol, .-symbol
|
||||
# define ASM_SYMBOL(symbol) symbol
|
||||
# define ASM_SYMBOL_INTERCEPTOR(symbol) symbol
|
||||
@ -87,9 +124,9 @@
|
||||
.globl __interceptor_trampoline_##name; \
|
||||
ASM_TYPE_FUNCTION(__interceptor_trampoline_##name); \
|
||||
__interceptor_trampoline_##name: \
|
||||
CFI_STARTPROC; \
|
||||
ASM_STARTPROC; \
|
||||
ASM_TAIL_CALL ASM_PREEMPTIBLE_SYM(__interceptor_##name); \
|
||||
CFI_ENDPROC; \
|
||||
ASM_ENDPROC; \
|
||||
ASM_SIZE(__interceptor_trampoline_##name)
|
||||
# define ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT 1
|
||||
# endif // Architecture supports interceptor trampoline
|
||||
|
||||
14
lib/tsan/sanitizer_common/sanitizer_atomic.h
vendored
14
lib/tsan/sanitizer_common/sanitizer_atomic.h
vendored
@ -18,12 +18,24 @@
|
||||
namespace __sanitizer {
|
||||
|
||||
enum memory_order {
|
||||
// If the __atomic atomic builtins are supported (Clang/GCC), use the
|
||||
// compiler provided macro values so that we can map the atomic operations
|
||||
// to __atomic_* directly.
|
||||
#ifdef __ATOMIC_SEQ_CST
|
||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||
memory_order_consume = __ATOMIC_CONSUME,
|
||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
||||
memory_order_release = __ATOMIC_RELEASE,
|
||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
||||
memory_order_seq_cst = __ATOMIC_SEQ_CST
|
||||
#else
|
||||
memory_order_relaxed = 1 << 0,
|
||||
memory_order_consume = 1 << 1,
|
||||
memory_order_acquire = 1 << 2,
|
||||
memory_order_release = 1 << 3,
|
||||
memory_order_acq_rel = 1 << 4,
|
||||
memory_order_seq_cst = 1 << 5
|
||||
#endif
|
||||
};
|
||||
|
||||
struct atomic_uint8_t {
|
||||
@ -49,7 +61,7 @@ struct atomic_uint32_t {
|
||||
struct atomic_uint64_t {
|
||||
typedef u64 Type;
|
||||
// On 32-bit platforms u64 is not necessary aligned on 8 bytes.
|
||||
volatile ALIGNED(8) Type val_dont_use;
|
||||
alignas(8) volatile Type val_dont_use;
|
||||
};
|
||||
|
||||
struct atomic_uintptr_t {
|
||||
|
||||
@ -14,60 +14,63 @@
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_H
|
||||
#define SANITIZER_ATOMIC_CLANG_H
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
# include "sanitizer_atomic_clang_x86.h"
|
||||
#else
|
||||
# include "sanitizer_atomic_clang_other.h"
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// We would like to just use compiler builtin atomic operations
|
||||
// for loads and stores, but they are mostly broken in clang:
|
||||
// - they lead to vastly inefficient code generation
|
||||
// (http://llvm.org/bugs/show_bug.cgi?id=17281)
|
||||
// - 64-bit atomic operations are not implemented on x86_32
|
||||
// (http://llvm.org/bugs/show_bug.cgi?id=15034)
|
||||
// - they are not implemented on ARM
|
||||
// error: undefined reference to '__atomic_load_4'
|
||||
// We use the compiler builtin atomic operations for loads and stores, which
|
||||
// generates correct code for all architectures, but may require libatomic
|
||||
// on platforms where e.g. 64-bit atomics are not supported natively.
|
||||
|
||||
// See http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
|
||||
// for mappings of the memory model to different processors.
|
||||
|
||||
inline void atomic_signal_fence(memory_order) {
|
||||
inline void atomic_signal_fence(memory_order mo) { __atomic_signal_fence(mo); }
|
||||
|
||||
inline void atomic_thread_fence(memory_order mo) { __atomic_thread_fence(mo); }
|
||||
|
||||
inline void proc_yield(int cnt) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
|
||||
inline void atomic_thread_fence(memory_order) {
|
||||
__sync_synchronize();
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
for (int i = 0; i < cnt; i++) __asm__ __volatile__("pause");
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename T::Type atomic_fetch_add(volatile T *a,
|
||||
typename T::Type v, memory_order mo) {
|
||||
inline typename T::Type atomic_load(const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo == memory_order_relaxed || mo == memory_order_consume ||
|
||||
mo == memory_order_acquire || mo == memory_order_seq_cst);
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return __atomic_load_n(&a->val_dont_use, mo);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo == memory_order_relaxed || mo == memory_order_release ||
|
||||
mo == memory_order_seq_cst);
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
__atomic_store_n(&a->val_dont_use, v, mo);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename T::Type atomic_fetch_add(volatile T *a, typename T::Type v,
|
||||
memory_order mo) {
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return __atomic_fetch_add(&a->val_dont_use, v, mo);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename T::Type atomic_fetch_sub(volatile T *a, typename T::Type v,
|
||||
memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return __sync_fetch_and_add(&a->val_dont_use, v);
|
||||
return __atomic_fetch_sub(&a->val_dont_use, v, mo);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename T::Type atomic_fetch_sub(volatile T *a,
|
||||
typename T::Type v, memory_order mo) {
|
||||
(void)mo;
|
||||
inline typename T::Type atomic_exchange(volatile T *a, typename T::Type v,
|
||||
memory_order mo) {
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return __sync_fetch_and_add(&a->val_dont_use, -v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Type atomic_exchange(volatile T *a,
|
||||
typename T::Type v, memory_order mo) {
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst))
|
||||
__sync_synchronize();
|
||||
v = __sync_lock_test_and_set(&a->val_dont_use, v);
|
||||
if (mo == memory_order_seq_cst)
|
||||
__sync_synchronize();
|
||||
return v;
|
||||
return __atomic_exchange_n(&a->val_dont_use, v, mo);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -83,8 +86,7 @@ inline bool atomic_compare_exchange_strong(volatile T *a, typename T::Type *cmp,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool atomic_compare_exchange_weak(volatile T *a,
|
||||
typename T::Type *cmp,
|
||||
inline bool atomic_compare_exchange_weak(volatile T *a, typename T::Type *cmp,
|
||||
typename T::Type xchg,
|
||||
memory_order mo) {
|
||||
return atomic_compare_exchange_strong(a, cmp, xchg, mo);
|
||||
@ -92,13 +94,6 @@ inline bool atomic_compare_exchange_weak(volatile T *a,
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
// This include provides explicit template instantiations for atomic_uint64_t
|
||||
// on MIPS32, which does not directly support 8 byte atomics. It has to
|
||||
// proceed the template definitions above.
|
||||
#if defined(_MIPS_SIM) && defined(_ABIO32) && _MIPS_SIM == _ABIO32
|
||||
# include "sanitizer_atomic_clang_mips.h"
|
||||
#endif
|
||||
|
||||
#undef ATOMIC_ORDER
|
||||
|
||||
#endif // SANITIZER_ATOMIC_CLANG_H
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
//===-- sanitizer_atomic_clang_mips.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_MIPS_H
|
||||
#define SANITIZER_ATOMIC_CLANG_MIPS_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// MIPS32 does not support atomics > 4 bytes. To address this lack of
|
||||
// functionality, the sanitizer library provides helper methods which use an
|
||||
// internal spin lock mechanism to emulate atomic operations when the size is
|
||||
// 8 bytes.
|
||||
static void __spin_lock(volatile int *lock) {
|
||||
while (__sync_lock_test_and_set(lock, 1))
|
||||
while (*lock) {
|
||||
}
|
||||
}
|
||||
|
||||
static void __spin_unlock(volatile int *lock) { __sync_lock_release(lock); }
|
||||
|
||||
// Make sure the lock is on its own cache line to prevent false sharing.
|
||||
// Put it inside a struct that is aligned and padded to the typical MIPS
|
||||
// cacheline which is 32 bytes.
|
||||
static struct {
|
||||
int lock;
|
||||
char pad[32 - sizeof(int)];
|
||||
} __attribute__((aligned(32))) lock = {0, {0}};
|
||||
|
||||
template <>
|
||||
inline atomic_uint64_t::Type atomic_fetch_add(volatile atomic_uint64_t *ptr,
|
||||
atomic_uint64_t::Type val,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_release | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
atomic_uint64_t::Type ret;
|
||||
|
||||
__spin_lock(&lock.lock);
|
||||
ret = *(const_cast<atomic_uint64_t::Type volatile *>(&ptr->val_dont_use));
|
||||
ptr->val_dont_use = ret + val;
|
||||
__spin_unlock(&lock.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline atomic_uint64_t::Type atomic_fetch_sub(volatile atomic_uint64_t *ptr,
|
||||
atomic_uint64_t::Type val,
|
||||
memory_order mo) {
|
||||
return atomic_fetch_add(ptr, -val, mo);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool atomic_compare_exchange_strong(volatile atomic_uint64_t *ptr,
|
||||
atomic_uint64_t::Type *cmp,
|
||||
atomic_uint64_t::Type xchg,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_release | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
typedef atomic_uint64_t::Type Type;
|
||||
Type cmpv = *cmp;
|
||||
Type prev;
|
||||
bool ret = false;
|
||||
|
||||
__spin_lock(&lock.lock);
|
||||
prev = *(const_cast<Type volatile *>(&ptr->val_dont_use));
|
||||
if (prev == cmpv) {
|
||||
ret = true;
|
||||
ptr->val_dont_use = xchg;
|
||||
}
|
||||
__spin_unlock(&lock.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline atomic_uint64_t::Type atomic_load(const volatile atomic_uint64_t *ptr,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_release | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
atomic_uint64_t::Type zero = 0;
|
||||
volatile atomic_uint64_t *Newptr =
|
||||
const_cast<volatile atomic_uint64_t *>(ptr);
|
||||
return atomic_fetch_add(Newptr, zero, mo);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void atomic_store(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type v,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_release | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
__spin_lock(&lock.lock);
|
||||
ptr->val_dont_use = v;
|
||||
__spin_unlock(&lock.lock);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ATOMIC_CLANG_MIPS_H
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
|
||||
#define SANITIZER_ATOMIC_CLANG_OTHER_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
|
||||
inline void proc_yield(int cnt) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Type atomic_load(
|
||||
const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_consume
|
||||
| memory_order_acquire | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
typename T::Type v;
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
v = a->val_dont_use;
|
||||
} else if (mo == memory_order_consume) {
|
||||
// Assume that processor respects data dependencies
|
||||
// (and that compiler won't break them).
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else if (mo == memory_order_acquire) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__sync_synchronize();
|
||||
} else { // seq_cst
|
||||
// E.g. on POWER we need a hw fence even before the store.
|
||||
__sync_synchronize();
|
||||
v = a->val_dont_use;
|
||||
__sync_synchronize();
|
||||
}
|
||||
} else {
|
||||
__atomic_load(const_cast<typename T::Type volatile *>(&a->val_dont_use), &v,
|
||||
__ATOMIC_SEQ_CST);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_release
|
||||
| memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
a->val_dont_use = v;
|
||||
} else if (mo == memory_order_release) {
|
||||
__sync_synchronize();
|
||||
a->val_dont_use = v;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else { // seq_cst
|
||||
__sync_synchronize();
|
||||
a->val_dont_use = v;
|
||||
__sync_synchronize();
|
||||
}
|
||||
} else {
|
||||
__atomic_store(&a->val_dont_use, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
|
||||
@ -1,113 +0,0 @@
|
||||
//===-- sanitizer_atomic_clang_x86.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_X86_H
|
||||
#define SANITIZER_ATOMIC_CLANG_X86_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
inline void proc_yield(int cnt) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
for (int i = 0; i < cnt; i++)
|
||||
__asm__ __volatile__("pause");
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Type atomic_load(
|
||||
const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_consume
|
||||
| memory_order_acquire | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
typename T::Type v;
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
v = a->val_dont_use;
|
||||
} else if (mo == memory_order_consume) {
|
||||
// Assume that processor respects data dependencies
|
||||
// (and that compiler won't break them).
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else if (mo == memory_order_acquire) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
// On x86 loads are implicitly acquire.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else { // seq_cst
|
||||
// On x86 plain MOV is enough for seq_cst store.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
} else {
|
||||
// 64-bit load on 32-bit platform.
|
||||
__asm__ __volatile__(
|
||||
"movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
|
||||
"movq %%mm0, %0;" // (ptr could be read-only)
|
||||
"emms;" // Empty mmx state/Reset FP regs
|
||||
: "=m" (v)
|
||||
: "m" (a->val_dont_use)
|
||||
: // mark the mmx registers as clobbered
|
||||
#ifdef __MMX__
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
#endif // #ifdef __MMX__
|
||||
"memory");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_release
|
||||
| memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
a->val_dont_use = v;
|
||||
} else if (mo == memory_order_release) {
|
||||
// On x86 stores are implicitly release.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
a->val_dont_use = v;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else { // seq_cst
|
||||
// On x86 stores are implicitly release.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
a->val_dont_use = v;
|
||||
__sync_synchronize();
|
||||
}
|
||||
} else {
|
||||
// 64-bit store on 32-bit platform.
|
||||
__asm__ __volatile__(
|
||||
"movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
|
||||
"movq %%mm0, %0;"
|
||||
"emms;" // Empty mmx state/Reset FP regs
|
||||
: "=m" (a->val_dont_use)
|
||||
: "m" (v)
|
||||
: // mark the mmx registers as clobbered
|
||||
#ifdef __MMX__
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
#endif // #ifdef __MMX__
|
||||
"memory");
|
||||
if (mo == memory_order_seq_cst)
|
||||
__sync_synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // #ifndef SANITIZER_ATOMIC_CLANG_X86_H
|
||||
@ -70,8 +70,8 @@ inline void proc_yield(int cnt) {
|
||||
template<typename T>
|
||||
inline typename T::Type atomic_load(
|
||||
const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_consume
|
||||
| memory_order_acquire | memory_order_seq_cst));
|
||||
DCHECK(mo == memory_order_relaxed || mo == memory_order_consume ||
|
||||
mo == memory_order_acquire || mo == memory_order_seq_cst);
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
typename T::Type v;
|
||||
// FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
|
||||
@ -87,8 +87,8 @@ inline typename T::Type atomic_load(
|
||||
|
||||
template<typename T>
|
||||
inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_release
|
||||
| memory_order_seq_cst));
|
||||
DCHECK(mo == memory_order_relaxed || mo == memory_order_release ||
|
||||
mo == memory_order_seq_cst);
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
// FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
|
||||
if (mo == memory_order_relaxed) {
|
||||
|
||||
@ -321,23 +321,23 @@ class TwoLevelBitVector {
|
||||
};
|
||||
|
||||
private:
|
||||
void check(uptr idx) const { CHECK_LE(idx, size()); }
|
||||
void check(uptr idx) const { CHECK_LT(idx, size()); }
|
||||
|
||||
uptr idx0(uptr idx) const {
|
||||
uptr res = idx / (BV::kSize * BV::kSize);
|
||||
CHECK_LE(res, kLevel1Size);
|
||||
CHECK_LT(res, kLevel1Size);
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr idx1(uptr idx) const {
|
||||
uptr res = (idx / BV::kSize) % BV::kSize;
|
||||
CHECK_LE(res, BV::kSize);
|
||||
CHECK_LT(res, BV::kSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr idx2(uptr idx) const {
|
||||
uptr res = idx % BV::kSize;
|
||||
CHECK_LE(res, BV::kSize);
|
||||
CHECK_LT(res, BV::kSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -139,9 +139,11 @@ u32 ChainedOriginDepot::Get(u32 id, u32 *other) {
|
||||
return desc.here_id;
|
||||
}
|
||||
|
||||
void ChainedOriginDepot::LockAll() { depot.LockAll(); }
|
||||
void ChainedOriginDepot::LockBeforeFork() { depot.LockBeforeFork(); }
|
||||
|
||||
void ChainedOriginDepot::UnlockAll() { depot.UnlockAll(); }
|
||||
void ChainedOriginDepot::UnlockAfterFork(bool fork_child) {
|
||||
depot.UnlockAfterFork(fork_child);
|
||||
}
|
||||
|
||||
void ChainedOriginDepot::TestOnlyUnmap() { depot.TestOnlyUnmap(); }
|
||||
|
||||
|
||||
@ -32,8 +32,8 @@ class ChainedOriginDepot {
|
||||
// Retrieves the stored StackDepot ID for the given origin ID.
|
||||
u32 Get(u32 id, u32 *other);
|
||||
|
||||
void LockAll();
|
||||
void UnlockAll();
|
||||
void LockBeforeFork();
|
||||
void UnlockAfterFork(bool fork_child);
|
||||
void TestOnlyUnmap();
|
||||
|
||||
private:
|
||||
|
||||
20
lib/tsan/sanitizer_common/sanitizer_common.cpp
vendored
20
lib/tsan/sanitizer_common/sanitizer_common.cpp
vendored
@ -115,8 +115,9 @@ void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
|
||||
if (!common_flags()->print_summary)
|
||||
return;
|
||||
InternalScopedString buff;
|
||||
buff.append("SUMMARY: %s: %s",
|
||||
alt_tool_name ? alt_tool_name : SanitizerToolName, error_message);
|
||||
buff.AppendF("SUMMARY: %s: %s",
|
||||
alt_tool_name ? alt_tool_name : SanitizerToolName,
|
||||
error_message);
|
||||
__sanitizer_report_error_summary(buff.data());
|
||||
}
|
||||
|
||||
@ -346,7 +347,13 @@ void RunMallocHooks(void *ptr, uptr size) {
|
||||
}
|
||||
}
|
||||
|
||||
void RunFreeHooks(void *ptr) {
|
||||
// Returns '1' if the call to free() should be ignored (based on
|
||||
// __sanitizer_ignore_free_hook), or '0' otherwise.
|
||||
int RunFreeHooks(void *ptr) {
|
||||
if (__sanitizer_ignore_free_hook(ptr)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
__sanitizer_free_hook(ptr);
|
||||
for (int i = 0; i < kMaxMallocFreeHooks; i++) {
|
||||
auto hook = MFHooks[i].free_hook;
|
||||
@ -354,6 +361,8 @@ void RunFreeHooks(void *ptr) {
|
||||
break;
|
||||
hook(ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
|
||||
@ -418,4 +427,9 @@ SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_WEAK_DEF(int, __sanitizer_ignore_free_hook, void *ptr) {
|
||||
(void)ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
45
lib/tsan/sanitizer_common/sanitizer_common.h
vendored
45
lib/tsan/sanitizer_common/sanitizer_common.h
vendored
@ -32,6 +32,7 @@ struct AddressInfo;
|
||||
struct BufferedStackTrace;
|
||||
struct SignalContext;
|
||||
struct StackTrace;
|
||||
struct SymbolizedStack;
|
||||
|
||||
// Constants.
|
||||
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
|
||||
@ -59,14 +60,10 @@ inline int Verbosity() {
|
||||
return atomic_load(¤t_verbosity, memory_order_relaxed);
|
||||
}
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
inline uptr GetPageSize() {
|
||||
// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array.
|
||||
return 4096;
|
||||
}
|
||||
inline uptr GetPageSizeCached() {
|
||||
return 4096;
|
||||
}
|
||||
#if SANITIZER_ANDROID && !defined(__aarch64__)
|
||||
// 32-bit Android only has 4k pages.
|
||||
inline uptr GetPageSize() { return 4096; }
|
||||
inline uptr GetPageSizeCached() { return 4096; }
|
||||
#else
|
||||
uptr GetPageSize();
|
||||
extern uptr PageSizeCached;
|
||||
@ -76,6 +73,7 @@ inline uptr GetPageSizeCached() {
|
||||
return PageSizeCached;
|
||||
}
|
||||
#endif
|
||||
|
||||
uptr GetMmapGranularity();
|
||||
uptr GetMaxVirtualAddress();
|
||||
uptr GetMaxUserVirtualAddress();
|
||||
@ -90,10 +88,11 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
|
||||
// Memory management
|
||||
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false);
|
||||
|
||||
inline void *MmapOrDieQuietly(uptr size, const char *mem_type) {
|
||||
return MmapOrDie(size, mem_type, /*raw_report*/ true);
|
||||
}
|
||||
void UnmapOrDie(void *addr, uptr size);
|
||||
void UnmapOrDie(void *addr, uptr size, bool raw_report = false);
|
||||
// Behaves just like MmapOrDie, but tolerates out of memory condition, in that
|
||||
// case returns nullptr.
|
||||
void *MmapOrDieOnFatalError(uptr size, const char *mem_type);
|
||||
@ -138,7 +137,8 @@ void UnmapFromTo(uptr from, uptr to);
|
||||
// shadow_size_bytes bytes on the right, which on linux is mapped no access.
|
||||
// The high_mem_end may be updated if the original shadow size doesn't fit.
|
||||
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
|
||||
uptr min_shadow_base_alignment, uptr &high_mem_end);
|
||||
uptr min_shadow_base_alignment, uptr &high_mem_end,
|
||||
uptr granularity);
|
||||
|
||||
// Let S = max(shadow_size, num_aliases * alias_size, ring_buffer_size).
|
||||
// Reserves 2*S bytes of address space to the right of the returned address and
|
||||
@ -177,7 +177,7 @@ bool DontDumpShadowMemory(uptr addr, uptr length);
|
||||
// Check if the built VMA size matches the runtime one.
|
||||
void CheckVMASize();
|
||||
void RunMallocHooks(void *ptr, uptr size);
|
||||
void RunFreeHooks(void *ptr);
|
||||
int RunFreeHooks(void *ptr);
|
||||
|
||||
class ReservedAddressRange {
|
||||
public:
|
||||
@ -208,6 +208,11 @@ void ParseUnixMemoryProfile(fill_profile_f cb, uptr *stats, char *smaps,
|
||||
// Simple low-level (mmap-based) allocator for internal use. Doesn't have
|
||||
// constructor, so all instances of LowLevelAllocator should be
|
||||
// linker initialized.
|
||||
//
|
||||
// NOTE: Users should instead use the singleton provided via
|
||||
// `GetGlobalLowLevelAllocator()` rather than create a new one. This way, the
|
||||
// number of mmap fragments can be reduced and use the same contiguous mmap
|
||||
// provided by this singleton.
|
||||
class LowLevelAllocator {
|
||||
public:
|
||||
// Requires an external lock.
|
||||
@ -224,6 +229,8 @@ typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size);
|
||||
// Passing NULL removes the callback.
|
||||
void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback);
|
||||
|
||||
LowLevelAllocator &GetGlobalLowLevelAllocator();
|
||||
|
||||
// IO
|
||||
void CatastrophicErrorWrite(const char *buffer, uptr length);
|
||||
void RawWrite(const char *buffer);
|
||||
@ -386,6 +393,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info,
|
||||
// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
|
||||
void ReportErrorSummary(const char *error_type, const StackTrace *trace,
|
||||
const char *alt_tool_name = nullptr);
|
||||
// Skips frames which we consider internal and not usefull to the users.
|
||||
const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames);
|
||||
|
||||
void ReportMmapWriteExec(int prot, int mflags);
|
||||
|
||||
@ -500,7 +509,7 @@ inline int ToLower(int c) {
|
||||
// A low-level vector based on mmap. May incur a significant memory overhead for
|
||||
// small vectors.
|
||||
// WARNING: The current implementation supports only POD types.
|
||||
template<typename T>
|
||||
template <typename T, bool raw_report = false>
|
||||
class InternalMmapVectorNoCtor {
|
||||
public:
|
||||
using value_type = T;
|
||||
@ -510,7 +519,7 @@ class InternalMmapVectorNoCtor {
|
||||
data_ = 0;
|
||||
reserve(initial_capacity);
|
||||
}
|
||||
void Destroy() { UnmapOrDie(data_, capacity_bytes_); }
|
||||
void Destroy() { UnmapOrDie(data_, capacity_bytes_, raw_report); }
|
||||
T &operator[](uptr i) {
|
||||
CHECK_LT(i, size_);
|
||||
return data_[i];
|
||||
@ -586,9 +595,10 @@ class InternalMmapVectorNoCtor {
|
||||
CHECK_LE(size_, new_capacity);
|
||||
uptr new_capacity_bytes =
|
||||
RoundUpTo(new_capacity * sizeof(T), GetPageSizeCached());
|
||||
T *new_data = (T *)MmapOrDie(new_capacity_bytes, "InternalMmapVector");
|
||||
T *new_data =
|
||||
(T *)MmapOrDie(new_capacity_bytes, "InternalMmapVector", raw_report);
|
||||
internal_memcpy(new_data, data_, size_ * sizeof(T));
|
||||
UnmapOrDie(data_, capacity_bytes_);
|
||||
UnmapOrDie(data_, capacity_bytes_, raw_report);
|
||||
data_ = new_data;
|
||||
capacity_bytes_ = new_capacity_bytes;
|
||||
}
|
||||
@ -636,7 +646,8 @@ class InternalScopedString {
|
||||
buffer_.resize(1);
|
||||
buffer_[0] = '\0';
|
||||
}
|
||||
void append(const char *format, ...) FORMAT(2, 3);
|
||||
void Append(const char *str);
|
||||
void AppendF(const char *format, ...) FORMAT(2, 3);
|
||||
const char *data() const { return buffer_.data(); }
|
||||
char *data() { return buffer_.data(); }
|
||||
|
||||
@ -1086,7 +1097,7 @@ inline u32 GetNumberOfCPUsCached() {
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
inline void *operator new(__sanitizer::operator_new_size_type size,
|
||||
inline void *operator new(__sanitizer::usize size,
|
||||
__sanitizer::LowLevelAllocator &alloc) {
|
||||
return alloc.Allocate(size);
|
||||
}
|
||||
|
||||
@ -33,16 +33,17 @@
|
||||
// COMMON_INTERCEPTOR_STRERROR
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_addrhashmap.h"
|
||||
#include "sanitizer_dl.h"
|
||||
#include "sanitizer_errno.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_platform_interceptors.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
#include "sanitizer_tls_get_addr.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#if SANITIZER_INTERCEPTOR_HOOKS
|
||||
#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) f(__VA_ARGS__);
|
||||
#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \
|
||||
@ -445,11 +446,13 @@ INTERCEPTOR(char*, textdomain, const char *domainname) {
|
||||
#define INIT_TEXTDOMAIN
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRCMP
|
||||
#if SANITIZER_INTERCEPT_STRCMP || SANITIZER_INTERCEPT_MEMCMP
|
||||
static inline int CharCmpX(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRCMP
|
||||
DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc,
|
||||
const char *s1, const char *s2, int result)
|
||||
|
||||
@ -971,7 +974,7 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
SSIZE_T res = REAL(read)(fd, ptr, count);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(read)(fd, ptr, count);
|
||||
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
@ -1006,7 +1009,7 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pread)(fd, ptr, count, offset);
|
||||
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
@ -1024,7 +1027,7 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pread64)(fd, ptr, count, offset);
|
||||
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
@ -1040,7 +1043,7 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov,
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = REAL(readv)(fd, iov, iovcnt);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(readv)(fd, iov, iovcnt);
|
||||
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
@ -1056,7 +1059,7 @@ INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(preadv)(fd, iov, iovcnt, offset);
|
||||
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
@ -1072,7 +1075,8 @@ INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset);
|
||||
SSIZE_T res =
|
||||
COMMON_INTERCEPTOR_BLOCK_REAL(preadv64)(fd, iov, iovcnt, offset);
|
||||
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
@ -1088,8 +1092,9 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) {
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(write)(fd, ptr, count);
|
||||
// FIXME: this check should be _before_ the call to REAL(write), not after
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(write)(fd, ptr, count);
|
||||
// FIXME: this check should be _before_ the call to
|
||||
// COMMON_INTERCEPTOR_BLOCK_REAL(write), not after
|
||||
if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
|
||||
return res;
|
||||
}
|
||||
@ -1118,7 +1123,7 @@ INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) {
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwrite)(fd, ptr, count, offset);
|
||||
if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
|
||||
return res;
|
||||
}
|
||||
@ -1134,7 +1139,7 @@ INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count,
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwrite64)(fd, ptr, count, offset);
|
||||
if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
|
||||
return res;
|
||||
}
|
||||
@ -1150,7 +1155,7 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov,
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(writev)(fd, iov, iovcnt);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(writev)(fd, iov, iovcnt);
|
||||
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
|
||||
return res;
|
||||
}
|
||||
@ -1166,7 +1171,7 @@ INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwritev)(fd, iov, iovcnt, offset);
|
||||
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
|
||||
return res;
|
||||
}
|
||||
@ -1182,7 +1187,8 @@ INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset);
|
||||
SSIZE_T res =
|
||||
COMMON_INTERCEPTOR_BLOCK_REAL(pwritev64)(fd, iov, iovcnt, offset);
|
||||
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
|
||||
return res;
|
||||
}
|
||||
@ -1245,6 +1251,7 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5);
|
||||
static const int PR_SET_NAME = 15;
|
||||
static const int PR_GET_NAME = 16;
|
||||
static const int PR_SET_VMA = 0x53564d41;
|
||||
static const int PR_SCHED_CORE = 62;
|
||||
static const int PR_SCHED_CORE_GET = 0;
|
||||
@ -1258,7 +1265,11 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
|
||||
internal_strncpy(buff, (char *)arg2, 15);
|
||||
buff[15] = 0;
|
||||
COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
|
||||
} else if (res != -1 && option == PR_SCHED_CORE && arg2 == PR_SCHED_CORE_GET) {
|
||||
} else if (res == 0 && option == PR_GET_NAME) {
|
||||
char *name = (char *)arg2;
|
||||
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));
|
||||
}
|
||||
return res;
|
||||
@ -2546,7 +2557,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(wait)(status);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait)(status);
|
||||
if (res != -1 && status)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
|
||||
return res;
|
||||
@ -2564,7 +2575,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop,
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(waitid)(idtype, id, infop, options);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(waitid)(idtype, id, infop, options);
|
||||
if (res != -1 && infop)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz);
|
||||
return res;
|
||||
@ -2575,7 +2586,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(waitpid)(pid, status, options);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(waitpid)(pid, status, options);
|
||||
if (res != -1 && status)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
|
||||
return res;
|
||||
@ -2586,7 +2597,7 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(wait3)(status, options, rusage);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait3)(status, options, rusage);
|
||||
if (res != -1) {
|
||||
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
|
||||
if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
|
||||
@ -2600,7 +2611,8 @@ INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(__wait4)(pid, status, options, rusage);
|
||||
int res =
|
||||
COMMON_INTERCEPTOR_BLOCK_REAL(__wait4)(pid, status, options, rusage);
|
||||
if (res != -1) {
|
||||
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
|
||||
if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
|
||||
@ -2615,7 +2627,7 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(wait4)(pid, status, options, rusage);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait4)(pid, status, options, rusage);
|
||||
if (res != -1) {
|
||||
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
|
||||
if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
|
||||
@ -2993,7 +3005,7 @@ INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
|
||||
addrlen0 = *addrlen;
|
||||
}
|
||||
int fd2 = REAL(accept)(fd, addr, addrlen);
|
||||
int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(accept)(fd, addr, addrlen);
|
||||
if (fd2 >= 0) {
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
|
||||
if (addr && addrlen)
|
||||
@ -3018,7 +3030,7 @@ INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int fd2 = REAL(accept4)(fd, addr, addrlen, f);
|
||||
int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(accept4)(fd, addr, addrlen, f);
|
||||
if (fd2 >= 0) {
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
|
||||
if (addr && addrlen)
|
||||
@ -3042,7 +3054,7 @@ INTERCEPTOR(int, paccept, int fd, void *addr, unsigned *addrlen,
|
||||
addrlen0 = *addrlen;
|
||||
}
|
||||
if (set) COMMON_INTERCEPTOR_READ_RANGE(ctx, set, sizeof(*set));
|
||||
int fd2 = REAL(paccept)(fd, addr, addrlen, set, f);
|
||||
int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(paccept)(fd, addr, addrlen, set, f);
|
||||
if (fd2 >= 0) {
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
|
||||
if (addr && addrlen)
|
||||
@ -3123,7 +3135,7 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg,
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recvmsg)(fd, msg, flags);
|
||||
if (res >= 0) {
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
if (msg) {
|
||||
@ -3144,7 +3156,8 @@ INTERCEPTOR(int, recvmmsg, int fd, struct __sanitizer_mmsghdr *msgvec,
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, recvmmsg, fd, msgvec, vlen, flags, timeout);
|
||||
if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz);
|
||||
int res = REAL(recvmmsg)(fd, msgvec, vlen, flags, timeout);
|
||||
int res =
|
||||
COMMON_INTERCEPTOR_BLOCK_REAL(recvmmsg)(fd, msgvec, vlen, flags, timeout);
|
||||
if (res >= 0) {
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
for (int i = 0; i < res; ++i) {
|
||||
@ -3222,7 +3235,7 @@ INTERCEPTOR(SSIZE_T, sendmsg, int fd, struct __sanitizer_msghdr *msg,
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
}
|
||||
SSIZE_T res = REAL(sendmsg)(fd, msg, flags);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(sendmsg)(fd, msg, flags);
|
||||
if (common_flags()->intercept_send && res >= 0 && msg)
|
||||
read_msghdr(ctx, msg, res);
|
||||
return res;
|
||||
@ -3241,7 +3254,7 @@ INTERCEPTOR(int, sendmmsg, int fd, struct __sanitizer_mmsghdr *msgvec,
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
}
|
||||
int res = REAL(sendmmsg)(fd, msgvec, vlen, flags);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(sendmmsg)(fd, msgvec, vlen, flags);
|
||||
if (res >= 0 && msgvec) {
|
||||
for (int i = 0; i < res; ++i) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &msgvec[i].msg_len,
|
||||
@ -3264,7 +3277,7 @@ INTERCEPTOR(int, msgsnd, int msqid, const void *msgp, SIZE_T msgsz,
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, msgsnd, msqid, msgp, msgsz, msgflg);
|
||||
if (msgp)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, msgp, sizeof(long) + msgsz);
|
||||
int res = REAL(msgsnd)(msqid, msgp, msgsz, msgflg);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(msgsnd)(msqid, msgp, msgsz, msgflg);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3272,7 +3285,8 @@ INTERCEPTOR(SSIZE_T, msgrcv, int msqid, void *msgp, SIZE_T msgsz,
|
||||
long msgtyp, int msgflg) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, msgrcv, msqid, msgp, msgsz, msgtyp, msgflg);
|
||||
SSIZE_T len = REAL(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg);
|
||||
SSIZE_T len =
|
||||
COMMON_INTERCEPTOR_BLOCK_REAL(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg);
|
||||
if (len != -1)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msgp, sizeof(long) + len);
|
||||
return len;
|
||||
@ -6116,7 +6130,7 @@ INTERCEPTOR(int, flopen, const char *path, int flags, ...) {
|
||||
if (path) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, path, internal_strlen(path) + 1);
|
||||
}
|
||||
return REAL(flopen)(path, flags, mode);
|
||||
return COMMON_INTERCEPTOR_BLOCK_REAL(flopen)(path, flags, mode);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) {
|
||||
@ -6129,7 +6143,7 @@ INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) {
|
||||
if (path) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, path, internal_strlen(path) + 1);
|
||||
}
|
||||
return REAL(flopenat)(dirfd, path, flags, mode);
|
||||
return COMMON_INTERCEPTOR_BLOCK_REAL(flopenat)(dirfd, path, flags, mode);
|
||||
}
|
||||
|
||||
#define INIT_FLOPEN \
|
||||
@ -6305,7 +6319,36 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
|
||||
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
|
||||
if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
|
||||
|
||||
if (filename) {
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
|
||||
|
||||
# if !SANITIZER_DYNAMIC
|
||||
// We care about a very specific use-case: dladdr on
|
||||
// statically-linked ASan may return <main program>
|
||||
// instead of the library.
|
||||
// We therefore only take effect if the sanitizer is statically
|
||||
// linked, and we don't bother canonicalizing paths because
|
||||
// dladdr should return the same address both times (we assume
|
||||
// the user did not canonicalize the result from dladdr).
|
||||
if (common_flags()->test_only_replace_dlopen_main_program) {
|
||||
VPrintf(1, "dlopen interceptor: filename: %s\n", filename);
|
||||
|
||||
const char *SelfFName = DladdrSelfFName();
|
||||
VPrintf(1, "dlopen interceptor: DladdrSelfFName: %p %s\n",
|
||||
(const void *)SelfFName, SelfFName);
|
||||
|
||||
if (SelfFName && internal_strcmp(SelfFName, filename) == 0) {
|
||||
// It's possible they copied the string from dladdr, so
|
||||
// we do a string comparison rather than pointer comparison.
|
||||
VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
|
||||
filename, SelfFName);
|
||||
filename = (char *)0; // RTLD_DEFAULT
|
||||
}
|
||||
}
|
||||
# endif // !SANITIZER_DYNAMIC
|
||||
}
|
||||
|
||||
void *res = COMMON_INTERCEPTOR_DLOPEN(filename, flag);
|
||||
Symbolizer::GetOrInit()->InvalidateModuleList();
|
||||
COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
|
||||
@ -6685,7 +6728,7 @@ INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, recv, fd, buf, len, flags);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = REAL(recv)(fd, buf, len, flags);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recv)(fd, buf, len, flags);
|
||||
if (res > 0) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len));
|
||||
}
|
||||
@ -6702,7 +6745,8 @@ INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags,
|
||||
SIZE_T srcaddr_sz;
|
||||
if (srcaddr) srcaddr_sz = *addrlen;
|
||||
(void)srcaddr_sz; // prevent "set but not used" warning
|
||||
SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recvfrom)(fd, buf, len, flags,
|
||||
srcaddr, addrlen);
|
||||
if (res > 0)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len));
|
||||
if (res >= 0 && srcaddr)
|
||||
@ -6725,7 +6769,7 @@ INTERCEPTOR(SSIZE_T, send, int fd, void *buf, SIZE_T len, int flags) {
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
}
|
||||
SSIZE_T res = REAL(send)(fd, buf, len, flags);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(send)(fd, buf, len, flags);
|
||||
if (common_flags()->intercept_send && res > 0)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len));
|
||||
return res;
|
||||
@ -6740,7 +6784,8 @@ INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags,
|
||||
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
}
|
||||
// Can't check dstaddr as it may have uninitialized padding at the end.
|
||||
SSIZE_T res = REAL(sendto)(fd, buf, len, flags, dstaddr, addrlen);
|
||||
SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(sendto)(fd, buf, len, flags,
|
||||
dstaddr, addrlen);
|
||||
if (common_flags()->intercept_send && res > 0)
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len));
|
||||
return res;
|
||||
@ -6753,25 +6798,25 @@ INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags,
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_EVENTFD_READ_WRITE
|
||||
INTERCEPTOR(int, eventfd_read, int fd, u64 *value) {
|
||||
INTERCEPTOR(int, eventfd_read, int fd, __sanitizer_eventfd_t *value) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, eventfd_read, fd, value);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
int res = REAL(eventfd_read)(fd, value);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(eventfd_read)(fd, value);
|
||||
if (res == 0) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, sizeof(*value));
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
INTERCEPTOR(int, eventfd_write, int fd, u64 value) {
|
||||
INTERCEPTOR(int, eventfd_write, int fd, __sanitizer_eventfd_t value) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, eventfd_write, fd, value);
|
||||
if (fd >= 0) {
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
}
|
||||
int res = REAL(eventfd_write)(fd, value);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(eventfd_write)(fd, value);
|
||||
return res;
|
||||
}
|
||||
#define INIT_EVENTFD_READ_WRITE \
|
||||
@ -7394,7 +7439,8 @@ INTERCEPTOR(int, open_by_handle_at, int mount_fd, struct file_handle* handle,
|
||||
COMMON_INTERCEPTOR_READ_RANGE(
|
||||
ctx, &sanitizer_handle->f_handle, sanitizer_handle->handle_bytes);
|
||||
|
||||
return REAL(open_by_handle_at)(mount_fd, handle, flags);
|
||||
return COMMON_INTERCEPTOR_BLOCK_REAL(open_by_handle_at)(mount_fd, handle,
|
||||
flags);
|
||||
}
|
||||
|
||||
#define INIT_OPEN_BY_HANDLE_AT COMMON_INTERCEPT_FUNCTION(open_by_handle_at)
|
||||
@ -7609,9 +7655,9 @@ static void write_protoent(void *ctx, struct __sanitizer_protoent *p) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->p_aliases, pp_size * sizeof(char *));
|
||||
}
|
||||
|
||||
INTERCEPTOR(struct __sanitizer_protoent *, getprotoent) {
|
||||
INTERCEPTOR(struct __sanitizer_protoent *, getprotoent,) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getprotoent);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getprotoent,);
|
||||
struct __sanitizer_protoent *p = REAL(getprotoent)();
|
||||
if (p)
|
||||
write_protoent(ctx, p);
|
||||
@ -7698,9 +7744,9 @@ INTERCEPTOR(int, getprotobynumber_r, int num,
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_NETENT
|
||||
INTERCEPTOR(struct __sanitizer_netent *, getnetent) {
|
||||
INTERCEPTOR(struct __sanitizer_netent *, getnetent,) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getnetent);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getnetent,);
|
||||
struct __sanitizer_netent *n = REAL(getnetent)();
|
||||
if (n) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
|
||||
@ -9862,9 +9908,9 @@ INTERCEPTOR(char *, fdevname_r, int fd, char *buf, SIZE_T len) {
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_GETUSERSHELL
|
||||
INTERCEPTOR(char *, getusershell) {
|
||||
INTERCEPTOR(char *, getusershell,) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getusershell);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getusershell,);
|
||||
char *res = REAL(getusershell)();
|
||||
if (res)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, internal_strlen(res) + 1);
|
||||
@ -9933,7 +9979,13 @@ INTERCEPTOR(void, sl_free, void *sl, int freeall) {
|
||||
INTERCEPTOR(SSIZE_T, getrandom, void *buf, SIZE_T buflen, unsigned int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getrandom, buf, buflen, flags);
|
||||
SSIZE_T n = REAL(getrandom)(buf, buflen, flags);
|
||||
// If GRND_NONBLOCK is set in the flags, it is non blocking.
|
||||
static const int grnd_nonblock = 1;
|
||||
SSIZE_T n;
|
||||
if ((flags & grnd_nonblock))
|
||||
n = REAL(getrandom)(buf, buflen, flags);
|
||||
else
|
||||
n = COMMON_INTERCEPTOR_BLOCK_REAL(getrandom)(buf, buflen, flags);
|
||||
if (n > 0) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, n);
|
||||
}
|
||||
@ -10180,20 +10232,6 @@ INTERCEPTOR(int, __xuname, int size, void *utsname) {
|
||||
#define INIT___XUNAME
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_HEXDUMP
|
||||
INTERCEPTOR(void, hexdump, const void *ptr, int length, const char *header, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, hexdump, ptr, length, header, flags);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, length);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, header, internal_strlen(header) + 1);
|
||||
REAL(hexdump)(ptr, length, header, flags);
|
||||
}
|
||||
|
||||
#define INIT_HEXDUMP COMMON_INTERCEPT_FUNCTION(hexdump);
|
||||
#else
|
||||
#define INIT_HEXDUMP
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_ARGP_PARSE
|
||||
INTERCEPTOR(int, argp_parse, const struct argp *argp, int argc, char **argv,
|
||||
unsigned flags, int *arg_index, void *input) {
|
||||
@ -10226,6 +10264,38 @@ INTERCEPTOR(int, cpuset_getaffinity, int level, int which, __int64_t id, SIZE_T
|
||||
#define INIT_CPUSET_GETAFFINITY
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_PREADV2
|
||||
INTERCEPTOR(SSIZE_T, preadv2, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
OFF_T offset, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, preadv2, fd, iov, iovcnt, offset, flags);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
SSIZE_T res = REAL(preadv2)(fd, iov, iovcnt, offset, flags);
|
||||
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
|
||||
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
return res;
|
||||
}
|
||||
#define INIT_PREADV2 COMMON_INTERCEPT_FUNCTION(preadv2)
|
||||
#else
|
||||
#define INIT_PREADV2
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_PWRITEV2
|
||||
INTERCEPTOR(SSIZE_T, pwritev2, int fd, __sanitizer_iovec *iov, int iovcnt,
|
||||
OFF_T offset, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pwritev2, fd, iov, iovcnt, offset, flags);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
|
||||
SSIZE_T res = REAL(pwritev2)(fd, iov, iovcnt, offset, flags);
|
||||
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
|
||||
return res;
|
||||
}
|
||||
#define INIT_PWRITEV2 COMMON_INTERCEPT_FUNCTION(pwritev2)
|
||||
#else
|
||||
#define INIT_PWRITEV2
|
||||
#endif
|
||||
|
||||
#include "sanitizer_common_interceptors_netbsd_compat.inc"
|
||||
|
||||
namespace __sanitizer {
|
||||
@ -10543,9 +10613,10 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_PROCCTL
|
||||
INIT_UNAME;
|
||||
INIT___XUNAME;
|
||||
INIT_HEXDUMP;
|
||||
INIT_ARGP_PARSE;
|
||||
INIT_CPUSET_GETAFFINITY;
|
||||
INIT_PREADV2;
|
||||
INIT_PWRITEV2;
|
||||
|
||||
INIT___PRINTF_CHK;
|
||||
}
|
||||
|
||||
@ -547,24 +547,25 @@ static void printf_common(void *ctx, const char *format, va_list aq) {
|
||||
continue;
|
||||
} else if (size == FSS_STRLEN) {
|
||||
if (void *argp = va_arg(aq, void *)) {
|
||||
uptr len;
|
||||
if (dir.starredPrecision) {
|
||||
// FIXME: properly support starred precision for strings.
|
||||
size = 0;
|
||||
len = 0;
|
||||
} else if (dir.fieldPrecision > 0) {
|
||||
// Won't read more than "precision" symbols.
|
||||
size = internal_strnlen((const char *)argp, dir.fieldPrecision);
|
||||
if (size < dir.fieldPrecision) size++;
|
||||
len = internal_strnlen((const char *)argp, dir.fieldPrecision);
|
||||
if (len < (uptr)dir.fieldPrecision)
|
||||
len++;
|
||||
} else {
|
||||
// Whole string will be accessed.
|
||||
size = internal_strlen((const char *)argp) + 1;
|
||||
len = internal_strlen((const char *)argp) + 1;
|
||||
}
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, len);
|
||||
}
|
||||
} else if (size == FSS_WCSLEN) {
|
||||
if (void *argp = va_arg(aq, void *)) {
|
||||
// FIXME: Properly support wide-character strings (via wcsrtombs).
|
||||
size = 0;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, 0);
|
||||
}
|
||||
} else {
|
||||
// Skip non-pointer args
|
||||
|
||||
@ -46,6 +46,7 @@ INTERFACE_FUNCTION(__sanitizer_purge_allocator)
|
||||
INTERFACE_FUNCTION(__sanitizer_print_memory_profile)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_free_hook)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_malloc_hook)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_ignore_free_hook)
|
||||
// Memintrinsic functions.
|
||||
INTERFACE_FUNCTION(__sanitizer_internal_memcpy)
|
||||
INTERFACE_FUNCTION(__sanitizer_internal_memmove)
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_code)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_data)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_frame)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_demangle)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_flush)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_set_demangle)
|
||||
|
||||
@ -87,8 +87,8 @@ void MaybeStartBackgroudThread() {
|
||||
if (!common_flags()->hard_rss_limit_mb &&
|
||||
!common_flags()->soft_rss_limit_mb &&
|
||||
!common_flags()->heap_profile) return;
|
||||
if (!&real_pthread_create) {
|
||||
VPrintf(1, "%s: real_pthread_create undefined\n", SanitizerToolName);
|
||||
if (!&internal_pthread_create) {
|
||||
VPrintf(1, "%s: internal_pthread_create undefined\n", SanitizerToolName);
|
||||
return; // Can't spawn the thread anyway.
|
||||
}
|
||||
|
||||
@ -119,8 +119,10 @@ void MaybeStartBackgroudThread() {}
|
||||
#endif
|
||||
|
||||
void WriteToSyslog(const char *msg) {
|
||||
if (!msg)
|
||||
return;
|
||||
InternalScopedString msg_copy;
|
||||
msg_copy.append("%s", msg);
|
||||
msg_copy.Append(msg);
|
||||
const char *p = msg_copy.data();
|
||||
|
||||
// Print one line at a time.
|
||||
@ -167,7 +169,7 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name,
|
||||
: !MmapFixedNoReserve(beg, size, name)) {
|
||||
Report(
|
||||
"ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
|
||||
"Perhaps you're using ulimit -v\n",
|
||||
"Perhaps you're using ulimit -v or ulimit -d\n",
|
||||
size);
|
||||
Abort();
|
||||
}
|
||||
|
||||
@ -38,6 +38,10 @@
|
||||
// Called before fork syscall.
|
||||
// COMMON_SYSCALL_POST_FORK(long res)
|
||||
// Called after fork syscall.
|
||||
// COMMON_SYSCALL_BLOCKING_START()
|
||||
// Called before blocking syscall.
|
||||
// COMMON_SYSCALL_BLOCKING_END()
|
||||
// Called after blocking syscall.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
@ -85,6 +89,16 @@
|
||||
{}
|
||||
# endif
|
||||
|
||||
# ifndef COMMON_SYSCALL_BLOCKING_START
|
||||
# define COMMON_SYSCALL_BLOCKING_START() \
|
||||
{}
|
||||
# endif
|
||||
|
||||
# ifndef COMMON_SYSCALL_BLOCKING_END
|
||||
# define COMMON_SYSCALL_BLOCKING_END() \
|
||||
{}
|
||||
# endif
|
||||
|
||||
// FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such).
|
||||
|
||||
extern "C" {
|
||||
@ -2808,6 +2822,15 @@ PRE_SYSCALL(fchownat)
|
||||
POST_SYSCALL(fchownat)
|
||||
(long res, long dfd, const void *filename, long user, long group, long flag) {}
|
||||
|
||||
PRE_SYSCALL(fchmodat2)(long dfd, const void *filename, long mode, long flag) {
|
||||
if (filename)
|
||||
PRE_READ(filename,
|
||||
__sanitizer::internal_strlen((const char *)filename) + 1);
|
||||
}
|
||||
|
||||
POST_SYSCALL(fchmodat2)
|
||||
(long res, long dfd, const void *filename, long mode, long flag) {}
|
||||
|
||||
PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) {
|
||||
if (filename)
|
||||
PRE_READ(filename,
|
||||
@ -3167,6 +3190,18 @@ POST_SYSCALL(sigaltstack)(long res, void *ss, void *oss) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRE_SYSCALL(futex)
|
||||
(void *uaddr, long futex_op, long val, void *timeout, void *uaddr2, long val3) {
|
||||
COMMON_SYSCALL_BLOCKING_START();
|
||||
}
|
||||
|
||||
POST_SYSCALL(futex)
|
||||
(long res, void *uaddr, long futex_op, long val, void *timeout, void *uaddr2,
|
||||
long val3) {
|
||||
COMMON_SYSCALL_BLOCKING_END();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
# undef PRE_SYSCALL
|
||||
|
||||
37
lib/tsan/sanitizer_common/sanitizer_dl.cpp
vendored
Normal file
37
lib/tsan/sanitizer_common/sanitizer_dl.cpp
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
//===-- sanitizer_dl.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 has helper functions that depend on libc's dynamic loading
|
||||
// introspection.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_dl.h"
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_GLIBC
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
extern const char *SanitizerToolName;
|
||||
|
||||
const char *DladdrSelfFName(void) {
|
||||
#if SANITIZER_GLIBC
|
||||
Dl_info info;
|
||||
int ret = dladdr((void *)&SanitizerToolName, &info);
|
||||
if (ret) {
|
||||
return info.dli_fname;
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
26
lib/tsan/sanitizer_common/sanitizer_dl.h
vendored
Normal file
26
lib/tsan/sanitizer_common/sanitizer_dl.h
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
//===-- sanitizer_dl.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 has helper functions that depend on libc's dynamic loading
|
||||
// introspection.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_DL_H
|
||||
#define SANITIZER_DL_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Returns the path to the shared object or - in the case of statically linked
|
||||
// sanitizers
|
||||
// - the main program itself, that contains the sanitizer.
|
||||
const char* DladdrSelfFName(void);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_DL_H
|
||||
4
lib/tsan/sanitizer_common/sanitizer_file.cpp
vendored
4
lib/tsan/sanitizer_common/sanitizer_file.cpp
vendored
@ -69,7 +69,7 @@ void ReportFile::ReopenIfNecessary() {
|
||||
WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
|
||||
WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
|
||||
char errmsg[100];
|
||||
internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)", err);
|
||||
internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)\n", err);
|
||||
WriteToFile(kStderrFd, errmsg, internal_strlen(errmsg));
|
||||
Die();
|
||||
}
|
||||
@ -88,6 +88,8 @@ static void RecursiveCreateParentDirs(char *path) {
|
||||
const char *ErrorMsgPrefix = "ERROR: Can't create directory: ";
|
||||
WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
|
||||
WriteToFile(kStderrFd, path, internal_strlen(path));
|
||||
const char *ErrorMsgSuffix = "\n";
|
||||
WriteToFile(kStderrFd, ErrorMsgSuffix, internal_strlen(ErrorMsgSuffix));
|
||||
Die();
|
||||
}
|
||||
path[i] = save;
|
||||
|
||||
2
lib/tsan/sanitizer_common/sanitizer_file.h
vendored
2
lib/tsan/sanitizer_common/sanitizer_file.h
vendored
@ -84,7 +84,7 @@ bool IsPathSeparator(const char c);
|
||||
bool IsAbsolutePath(const char *path);
|
||||
// Returns true on success, false on failure.
|
||||
bool CreateDir(const char *pathname);
|
||||
// Starts a subprocess and returs its pid.
|
||||
// Starts a subprocess and returns its pid.
|
||||
// If *_fd parameters are not kInvalidFd their corresponding input/output
|
||||
// streams will be redirect to the file. The files will always be closed
|
||||
// in parent process even in case of an error.
|
||||
|
||||
@ -19,8 +19,6 @@
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
LowLevelAllocator FlagParser::Alloc;
|
||||
|
||||
class UnknownFlags {
|
||||
static const int kMaxUnknownFlags = 20;
|
||||
const char *unknown_flags_[kMaxUnknownFlags];
|
||||
@ -49,7 +47,7 @@ void ReportUnrecognizedFlags() {
|
||||
|
||||
char *FlagParser::ll_strndup(const char *s, uptr n) {
|
||||
uptr len = internal_strnlen(s, n);
|
||||
char *s2 = (char*)Alloc.Allocate(len + 1);
|
||||
char *s2 = (char *)GetGlobalLowLevelAllocator().Allocate(len + 1);
|
||||
internal_memcpy(s2, s, len);
|
||||
s2[len] = 0;
|
||||
return s2;
|
||||
@ -185,7 +183,8 @@ void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
|
||||
}
|
||||
|
||||
FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
|
||||
flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
|
||||
flags_ =
|
||||
(Flag *)GetGlobalLowLevelAllocator().Allocate(sizeof(Flag) * kMaxFlags);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
@ -178,8 +178,6 @@ class FlagParser {
|
||||
bool ParseFile(const char *path, bool ignore_missing);
|
||||
void PrintFlagDescriptions();
|
||||
|
||||
static LowLevelAllocator Alloc;
|
||||
|
||||
private:
|
||||
void fatal_error(const char *err);
|
||||
bool is_space(char c);
|
||||
@ -193,7 +191,7 @@ class FlagParser {
|
||||
template <typename T>
|
||||
static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
|
||||
T *var) {
|
||||
FlagHandler<T> *fh = new (FlagParser::Alloc) FlagHandler<T>(var);
|
||||
FlagHandler<T> *fh = new (GetGlobalLowLevelAllocator()) FlagHandler<T>(var);
|
||||
parser->RegisterHandler(name, fh, desc);
|
||||
}
|
||||
|
||||
|
||||
@ -108,11 +108,11 @@ class FlagHandlerInclude final : public FlagHandlerBase {
|
||||
};
|
||||
|
||||
void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
|
||||
FlagHandlerInclude *fh_include = new (FlagParser::Alloc)
|
||||
FlagHandlerInclude *fh_include = new (GetGlobalLowLevelAllocator())
|
||||
FlagHandlerInclude(parser, /*ignore_missing*/ false);
|
||||
parser->RegisterHandler("include", fh_include,
|
||||
"read more options from the given file");
|
||||
FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc)
|
||||
FlagHandlerInclude *fh_include_if_exists = new (GetGlobalLowLevelAllocator())
|
||||
FlagHandlerInclude(parser, /*ignore_missing*/ true);
|
||||
parser->RegisterHandler(
|
||||
"include_if_exists", fh_include_if_exists,
|
||||
|
||||
13
lib/tsan/sanitizer_common/sanitizer_flags.inc
vendored
13
lib/tsan/sanitizer_common/sanitizer_flags.inc
vendored
@ -269,3 +269,16 @@ COMMON_FLAG(bool, detect_write_exec, false,
|
||||
COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
|
||||
"TEST ONLY fail to read memory mappings to emulate sanitized "
|
||||
"\"init\"")
|
||||
// With static linking, dladdr((void*)pthread_join) or similar will return the
|
||||
// path to the main program. This flag will replace dlopen(<main program,...>
|
||||
// with dlopen(NULL,...), which is the correct way to get a handle to the main
|
||||
// program.
|
||||
COMMON_FLAG(bool, test_only_replace_dlopen_main_program, false,
|
||||
"TEST ONLY replace dlopen(<main program>,...) with dlopen(NULL)")
|
||||
|
||||
COMMON_FLAG(bool, enable_symbolizer_markup, SANITIZER_FUCHSIA,
|
||||
"Use sanitizer symbolizer markup, available on Linux "
|
||||
"and always set true for Fuchsia.")
|
||||
|
||||
COMMON_FLAG(bool, detect_invalid_join, true,
|
||||
"If set, check invalid joins of threads.")
|
||||
|
||||
@ -109,6 +109,10 @@ class TwoLevelMap {
|
||||
return *AddressSpaceView::LoadWritable(&map2[idx % kSize2]);
|
||||
}
|
||||
|
||||
void Lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mu_.Lock(); }
|
||||
|
||||
void Unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mu_.Unlock(); }
|
||||
|
||||
private:
|
||||
constexpr uptr MmapSize() const {
|
||||
return RoundUpTo(kSize2 * sizeof(T), GetPageSizeCached());
|
||||
|
||||
137
lib/tsan/sanitizer_common/sanitizer_freebsd.h
vendored
137
lib/tsan/sanitizer_common/sanitizer_freebsd.h
vendored
@ -1,137 +0,0 @@
|
||||
//===-- sanitizer_freebsd.h -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime. It contains FreeBSD-specific
|
||||
// definitions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_FREEBSD_H
|
||||
#define SANITIZER_FREEBSD_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
|
||||
// 32-bit mode.
|
||||
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||
#include <osreldate.h>
|
||||
#if __FreeBSD_version <= 902001 // v9.2
|
||||
#include <link.h>
|
||||
#include <sys/param.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
typedef unsigned long long __xuint64_t;
|
||||
|
||||
typedef __int32_t __xregister_t;
|
||||
|
||||
typedef struct __xmcontext {
|
||||
__xregister_t mc_onstack;
|
||||
__xregister_t mc_gs;
|
||||
__xregister_t mc_fs;
|
||||
__xregister_t mc_es;
|
||||
__xregister_t mc_ds;
|
||||
__xregister_t mc_edi;
|
||||
__xregister_t mc_esi;
|
||||
__xregister_t mc_ebp;
|
||||
__xregister_t mc_isp;
|
||||
__xregister_t mc_ebx;
|
||||
__xregister_t mc_edx;
|
||||
__xregister_t mc_ecx;
|
||||
__xregister_t mc_eax;
|
||||
__xregister_t mc_trapno;
|
||||
__xregister_t mc_err;
|
||||
__xregister_t mc_eip;
|
||||
__xregister_t mc_cs;
|
||||
__xregister_t mc_eflags;
|
||||
__xregister_t mc_esp;
|
||||
__xregister_t mc_ss;
|
||||
|
||||
int mc_len;
|
||||
int mc_fpformat;
|
||||
int mc_ownedfp;
|
||||
__xregister_t mc_flags;
|
||||
|
||||
int mc_fpstate[128] __aligned(16);
|
||||
__xregister_t mc_fsbase;
|
||||
__xregister_t mc_gsbase;
|
||||
__xregister_t mc_xfpustate;
|
||||
__xregister_t mc_xfpustate_len;
|
||||
|
||||
int mc_spare2[4];
|
||||
} xmcontext_t;
|
||||
|
||||
typedef struct __xucontext {
|
||||
sigset_t uc_sigmask;
|
||||
xmcontext_t uc_mcontext;
|
||||
|
||||
struct __ucontext *uc_link;
|
||||
stack_t uc_stack;
|
||||
int uc_flags;
|
||||
int __spare__[4];
|
||||
} xucontext_t;
|
||||
|
||||
struct xkinfo_vmentry {
|
||||
int kve_structsize;
|
||||
int kve_type;
|
||||
__xuint64_t kve_start;
|
||||
__xuint64_t kve_end;
|
||||
__xuint64_t kve_offset;
|
||||
__xuint64_t kve_vn_fileid;
|
||||
__uint32_t kve_vn_fsid;
|
||||
int kve_flags;
|
||||
int kve_resident;
|
||||
int kve_private_resident;
|
||||
int kve_protection;
|
||||
int kve_ref_count;
|
||||
int kve_shadow_count;
|
||||
int kve_vn_type;
|
||||
__xuint64_t kve_vn_size;
|
||||
__uint32_t kve_vn_rdev;
|
||||
__uint16_t kve_vn_mode;
|
||||
__uint16_t kve_status;
|
||||
int _kve_ispare[12];
|
||||
char kve_path[PATH_MAX];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
__uint32_t p_type;
|
||||
__uint32_t p_offset;
|
||||
__uint32_t p_vaddr;
|
||||
__uint32_t p_paddr;
|
||||
__uint32_t p_filesz;
|
||||
__uint32_t p_memsz;
|
||||
__uint32_t p_flags;
|
||||
__uint32_t p_align;
|
||||
} XElf32_Phdr;
|
||||
|
||||
struct xdl_phdr_info {
|
||||
Elf_Addr dlpi_addr;
|
||||
const char *dlpi_name;
|
||||
const XElf32_Phdr *dlpi_phdr;
|
||||
Elf_Half dlpi_phnum;
|
||||
unsigned long long int dlpi_adds;
|
||||
unsigned long long int dlpi_subs;
|
||||
size_t dlpi_tls_modid;
|
||||
void *dlpi_tls_data;
|
||||
};
|
||||
|
||||
typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info *, size_t,
|
||||
void *);
|
||||
typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void *);
|
||||
|
||||
#define xdl_iterate_phdr(callback, param) \
|
||||
(((xdl_iterate_phdr_t *)dl_iterate_phdr)((callback), (param)))
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // __FreeBSD_version <= 902001
|
||||
#endif // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||
|
||||
#endif // SANITIZER_FREEBSD_H
|
||||
107
lib/tsan/sanitizer_common/sanitizer_fuchsia.cpp
vendored
107
lib/tsan/sanitizer_common/sanitizer_fuchsia.cpp
vendored
@ -129,6 +129,60 @@ uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }
|
||||
|
||||
bool ErrorIsOOM(error_t err) { return err == ZX_ERR_NO_MEMORY; }
|
||||
|
||||
// For any sanitizer internal that needs to map something which can be unmapped
|
||||
// later, first attempt to map to a pre-allocated VMAR. This helps reduce
|
||||
// fragmentation from many small anonymous mmap calls. A good value for this
|
||||
// VMAR size would be the total size of your typical sanitizer internal objects
|
||||
// allocated in an "average" process lifetime. Examples of this include:
|
||||
// FakeStack, LowLevelAllocator mappings, TwoLevelMap, InternalMmapVector,
|
||||
// StackStore, CreateAsanThread, etc.
|
||||
//
|
||||
// This is roughly equal to the total sum of sanitizer internal mappings for a
|
||||
// large test case.
|
||||
constexpr size_t kSanitizerHeapVmarSize = 13ULL << 20;
|
||||
static zx_handle_t gSanitizerHeapVmar = ZX_HANDLE_INVALID;
|
||||
|
||||
static zx_status_t GetSanitizerHeapVmar(zx_handle_t *vmar) {
|
||||
zx_status_t status = ZX_OK;
|
||||
if (gSanitizerHeapVmar == ZX_HANDLE_INVALID) {
|
||||
CHECK_EQ(kSanitizerHeapVmarSize % GetPageSizeCached(), 0);
|
||||
uintptr_t base;
|
||||
status = _zx_vmar_allocate(
|
||||
_zx_vmar_root_self(),
|
||||
ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,
|
||||
kSanitizerHeapVmarSize, &gSanitizerHeapVmar, &base);
|
||||
}
|
||||
*vmar = gSanitizerHeapVmar;
|
||||
if (status == ZX_OK)
|
||||
CHECK_NE(gSanitizerHeapVmar, ZX_HANDLE_INVALID);
|
||||
return status;
|
||||
}
|
||||
|
||||
static zx_status_t TryVmoMapSanitizerVmar(zx_vm_option_t options,
|
||||
size_t vmar_offset, zx_handle_t vmo,
|
||||
size_t size, uintptr_t *addr,
|
||||
zx_handle_t *vmar_used = nullptr) {
|
||||
zx_handle_t vmar;
|
||||
zx_status_t status = GetSanitizerHeapVmar(&vmar);
|
||||
if (status != ZX_OK)
|
||||
return status;
|
||||
|
||||
status = _zx_vmar_map(gSanitizerHeapVmar, options, vmar_offset, vmo,
|
||||
/*vmo_offset=*/0, size, addr);
|
||||
if (vmar_used)
|
||||
*vmar_used = gSanitizerHeapVmar;
|
||||
if (status == ZX_ERR_NO_RESOURCES || status == ZX_ERR_INVALID_ARGS) {
|
||||
// This means there's no space in the heap VMAR, so fallback to the root
|
||||
// VMAR.
|
||||
status = _zx_vmar_map(_zx_vmar_root_self(), options, vmar_offset, vmo,
|
||||
/*vmo_offset=*/0, size, addr);
|
||||
if (vmar_used)
|
||||
*vmar_used = _zx_vmar_root_self();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
|
||||
bool raw_report, bool die_for_nomem) {
|
||||
size = RoundUpTo(size, GetPageSize());
|
||||
@ -144,11 +198,9 @@ static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
|
||||
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
|
||||
internal_strlen(mem_type));
|
||||
|
||||
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
|
||||
uintptr_t addr;
|
||||
status =
|
||||
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
|
||||
vmo, 0, size, &addr);
|
||||
status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
|
||||
/*vmar_offset=*/0, vmo, size, &addr);
|
||||
_zx_handle_close(vmo);
|
||||
|
||||
if (status != ZX_OK) {
|
||||
@ -226,27 +278,32 @@ static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size,
|
||||
|
||||
uptr ReservedAddressRange::Map(uptr fixed_addr, uptr map_size,
|
||||
const char *name) {
|
||||
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_, name_,
|
||||
false);
|
||||
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
|
||||
name ? name : name_, false);
|
||||
}
|
||||
|
||||
uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size,
|
||||
const char *name) {
|
||||
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_, name_, true);
|
||||
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
|
||||
name ? name : name_, true);
|
||||
}
|
||||
|
||||
void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
|
||||
void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar,
|
||||
bool raw_report) {
|
||||
if (!addr || !size)
|
||||
return;
|
||||
size = RoundUpTo(size, GetPageSize());
|
||||
|
||||
zx_status_t status =
|
||||
_zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
|
||||
if (status != ZX_OK) {
|
||||
Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
|
||||
SanitizerToolName, size, size, addr);
|
||||
CHECK("unable to unmap" && 0);
|
||||
if (status == ZX_ERR_INVALID_ARGS && target_vmar == gSanitizerHeapVmar) {
|
||||
// If there wasn't any space in the heap vmar, the fallback was the root
|
||||
// vmar.
|
||||
status = _zx_vmar_unmap(_zx_vmar_root_self(),
|
||||
reinterpret_cast<uintptr_t>(addr), size);
|
||||
}
|
||||
if (status != ZX_OK)
|
||||
ReportMunmapFailureAndDie(addr, size, status, raw_report);
|
||||
|
||||
DecreaseTotalMmap(size);
|
||||
}
|
||||
@ -268,7 +325,8 @@ void ReservedAddressRange::Unmap(uptr addr, uptr size) {
|
||||
}
|
||||
// Partial unmapping does not affect the fact that the initial range is still
|
||||
// reserved, and the resulting unmapped memory can't be reused.
|
||||
UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
|
||||
UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar,
|
||||
/*raw_report=*/false);
|
||||
}
|
||||
|
||||
// This should never be called.
|
||||
@ -307,17 +365,16 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
|
||||
internal_strlen(mem_type));
|
||||
|
||||
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
|
||||
|
||||
// Map a larger size to get a chunk of address space big enough that
|
||||
// it surely contains an aligned region of the requested size. Then
|
||||
// overwrite the aligned middle portion with a mapping from the
|
||||
// beginning of the VMO, and unmap the excess before and after.
|
||||
size_t map_size = size + alignment;
|
||||
uintptr_t addr;
|
||||
status =
|
||||
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
|
||||
vmo, 0, map_size, &addr);
|
||||
zx_handle_t vmar_used;
|
||||
status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
|
||||
/*vmar_offset=*/0, vmo, map_size, &addr,
|
||||
&vmar_used);
|
||||
if (status == ZX_OK) {
|
||||
uintptr_t map_addr = addr;
|
||||
uintptr_t map_end = map_addr + map_size;
|
||||
@ -325,12 +382,12 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
uintptr_t end = addr + size;
|
||||
if (addr != map_addr) {
|
||||
zx_info_vmar_t info;
|
||||
status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
|
||||
sizeof(info), NULL, NULL);
|
||||
status = _zx_object_get_info(vmar_used, ZX_INFO_VMAR, &info, sizeof(info),
|
||||
NULL, NULL);
|
||||
if (status == ZX_OK) {
|
||||
uintptr_t new_addr;
|
||||
status = _zx_vmar_map(
|
||||
_zx_vmar_root_self(),
|
||||
vmar_used,
|
||||
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
|
||||
addr - info.base, vmo, 0, size, &new_addr);
|
||||
if (status == ZX_OK)
|
||||
@ -338,9 +395,9 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
}
|
||||
}
|
||||
if (status == ZX_OK && addr != map_addr)
|
||||
status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr);
|
||||
status = _zx_vmar_unmap(vmar_used, map_addr, addr - map_addr);
|
||||
if (status == ZX_OK && end != map_end)
|
||||
status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end);
|
||||
status = _zx_vmar_unmap(vmar_used, end, map_end - end);
|
||||
}
|
||||
_zx_handle_close(vmo);
|
||||
|
||||
@ -355,8 +412,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
return reinterpret_cast<void *>(addr);
|
||||
}
|
||||
|
||||
void UnmapOrDie(void *addr, uptr size) {
|
||||
UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
|
||||
void UnmapOrDie(void *addr, uptr size, bool raw_report) {
|
||||
UnmapOrDieVmar(addr, size, gSanitizerHeapVmar, raw_report);
|
||||
}
|
||||
|
||||
void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
|
||||
|
||||
@ -15,6 +15,11 @@
|
||||
#include "sanitizer_platform.h"
|
||||
#include "sanitizer_redefine_builtins.h"
|
||||
|
||||
// GCC does not understand __has_feature.
|
||||
#if !defined(__has_feature)
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef SANITIZER_DEBUG
|
||||
# define SANITIZER_DEBUG 0
|
||||
#endif
|
||||
@ -30,13 +35,20 @@
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
|
||||
#endif
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_IMPORT
|
||||
#elif SANITIZER_GO
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
# define SANITIZER_WEAK_IMPORT
|
||||
#else
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
|
||||
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
|
||||
#endif
|
||||
# if SANITIZER_APPLE
|
||||
# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak_import))
|
||||
# else
|
||||
# define SANITIZER_WEAK_IMPORT extern "C" SANITIZER_WEAK_ATTRIBUTE
|
||||
# endif // SANITIZER_APPLE
|
||||
#endif // SANITIZER_WINDOWS
|
||||
|
||||
//--------------------------- WEAK FUNCTIONS ---------------------------------//
|
||||
// When working with weak functions, to simplify the code and make it more
|
||||
@ -179,14 +191,18 @@ typedef uptr OFF_T;
|
||||
#endif
|
||||
typedef u64 OFF64_T;
|
||||
|
||||
#if (SANITIZER_WORDSIZE == 64) || SANITIZER_APPLE
|
||||
typedef uptr operator_new_size_type;
|
||||
#ifdef __SIZE_TYPE__
|
||||
typedef __SIZE_TYPE__ usize;
|
||||
#else
|
||||
# if defined(__s390__) && !defined(__s390x__)
|
||||
// Special case: 31-bit s390 has unsigned long as size_t.
|
||||
typedef unsigned long operator_new_size_type;
|
||||
// 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 u32 operator_new_size_type;
|
||||
typedef uptr usize;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
16
lib/tsan/sanitizer_common/sanitizer_libc.cpp
vendored
16
lib/tsan/sanitizer_common/sanitizer_libc.cpp
vendored
@ -199,6 +199,14 @@ char *internal_strncat(char *dst, const char *src, uptr n) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
wchar_t *internal_wcscpy(wchar_t *dst, const wchar_t *src) {
|
||||
wchar_t *dst_it = dst;
|
||||
do {
|
||||
*dst_it++ = *src++;
|
||||
} while (*src);
|
||||
return dst;
|
||||
}
|
||||
|
||||
uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) {
|
||||
const uptr srclen = internal_strlen(src);
|
||||
if (srclen < maxlen) {
|
||||
@ -218,6 +226,14 @@ char *internal_strncpy(char *dst, const char *src, uptr n) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
wchar_t *internal_wcsncpy(wchar_t *dst, const wchar_t *src, uptr n) {
|
||||
uptr i;
|
||||
for (i = 0; i < n && src[i]; ++i)
|
||||
dst[i] = src[i];
|
||||
internal_memset(dst + i, 0, (n - i) * sizeof(wchar_t));
|
||||
return dst;
|
||||
}
|
||||
|
||||
uptr internal_strnlen(const char *s, uptr maxlen) {
|
||||
uptr i = 0;
|
||||
while (i < maxlen && s[i]) i++;
|
||||
|
||||
3
lib/tsan/sanitizer_common/sanitizer_libc.h
vendored
3
lib/tsan/sanitizer_common/sanitizer_libc.h
vendored
@ -71,7 +71,8 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...)
|
||||
FORMAT(3, 4);
|
||||
uptr internal_wcslen(const wchar_t *s);
|
||||
uptr internal_wcsnlen(const wchar_t *s, uptr maxlen);
|
||||
|
||||
wchar_t *internal_wcscpy(wchar_t *dst, const wchar_t *src);
|
||||
wchar_t *internal_wcsncpy(wchar_t *dst, const wchar_t *src, uptr maxlen);
|
||||
// Return true if all bytes in [mem, mem+size) are zero.
|
||||
// Optimized for the case when the result is true.
|
||||
bool mem_is_zero(const char *mem, uptr size);
|
||||
|
||||
@ -105,8 +105,8 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
continue;
|
||||
if (IsPcInstrumented(range.beg) && IsPcInstrumented(range.end - 1))
|
||||
continue;
|
||||
VReport(1, "Adding instrumented range 0x%zx-0x%zx from library '%s'\n",
|
||||
range.beg, range.end, mod.full_name());
|
||||
VReport(1, "Adding instrumented range %p-%p from library '%s'\n",
|
||||
(void *)range.beg, (void *)range.end, mod.full_name());
|
||||
const uptr idx =
|
||||
atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
|
||||
CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
|
||||
|
||||
394
lib/tsan/sanitizer_common/sanitizer_linux.cpp
vendored
394
lib/tsan/sanitizer_common/sanitizer_linux.cpp
vendored
@ -33,11 +33,15 @@
|
||||
// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
|
||||
// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
|
||||
// access stat from asm/stat.h, without conflicting with definition in
|
||||
// sys/stat.h, we use this trick.
|
||||
#if SANITIZER_MIPS64
|
||||
// sys/stat.h, we use this trick. sparc64 is similar, using
|
||||
// syscall(__NR_stat64) and struct kernel_stat64.
|
||||
# if SANITIZER_LINUX && (SANITIZER_MIPS64 || SANITIZER_SPARC64)
|
||||
# include <asm/unistd.h>
|
||||
# include <sys/types.h>
|
||||
# define stat kernel_stat
|
||||
# if SANITIZER_SPARC64
|
||||
# define stat64 kernel_stat64
|
||||
# endif
|
||||
# if SANITIZER_GO
|
||||
# undef st_atime
|
||||
# undef st_mtime
|
||||
@ -48,6 +52,7 @@
|
||||
# endif
|
||||
# include <asm/stat.h>
|
||||
# undef stat
|
||||
# undef stat64
|
||||
# endif
|
||||
|
||||
# include <dlfcn.h>
|
||||
@ -58,7 +63,6 @@
|
||||
# include <sched.h>
|
||||
# include <signal.h>
|
||||
# include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
# if !SANITIZER_SOLARIS
|
||||
# include <sys/ptrace.h>
|
||||
# endif
|
||||
@ -83,10 +87,10 @@
|
||||
# endif
|
||||
|
||||
# if SANITIZER_FREEBSD
|
||||
# include <machine/atomic.h>
|
||||
# include <sys/exec.h>
|
||||
# include <sys/procctl.h>
|
||||
# include <sys/sysctl.h>
|
||||
#include <machine/atomic.h>
|
||||
extern "C" {
|
||||
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
|
||||
// FreeBSD 9.2 and 10.0.
|
||||
@ -97,8 +101,8 @@ extern "C" {
|
||||
|
||||
# if SANITIZER_NETBSD
|
||||
# include <limits.h> // For NAME_MAX
|
||||
#include <sys/sysctl.h>
|
||||
# include <sys/exec.h>
|
||||
# include <sys/sysctl.h>
|
||||
extern struct ps_strings *__ps_strings;
|
||||
# endif // SANITIZER_NETBSD
|
||||
|
||||
@ -136,9 +140,7 @@ const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
|
||||
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
|
||||
# endif
|
||||
|
||||
// Note : FreeBSD had implemented both
|
||||
// Linux apis, available from
|
||||
// future 12.x version most likely
|
||||
// Note : FreeBSD implemented both Linux and OpenBSD apis.
|
||||
# if SANITIZER_LINUX && defined(__NR_getrandom)
|
||||
# if !defined(GRND_NONBLOCK)
|
||||
# define GRND_NONBLOCK 1
|
||||
@ -148,10 +150,8 @@ const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
|
||||
# define SANITIZER_USE_GETRANDOM 0
|
||||
# endif // SANITIZER_LINUX && defined(__NR_getrandom)
|
||||
|
||||
#if SANITIZER_FREEBSD && __FreeBSD_version >= 1200000
|
||||
# if SANITIZER_FREEBSD
|
||||
# define SANITIZER_USE_GETENTROPY 1
|
||||
#else
|
||||
# define SANITIZER_USE_GETENTROPY 0
|
||||
# endif
|
||||
|
||||
namespace __sanitizer {
|
||||
@ -160,6 +160,7 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {
|
||||
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset));
|
||||
}
|
||||
|
||||
// Block asynchronous signals
|
||||
void BlockSignals(__sanitizer_sigset_t *oldset) {
|
||||
__sanitizer_sigset_t set;
|
||||
internal_sigfillset(&set);
|
||||
@ -174,7 +175,17 @@ void BlockSignals(__sanitizer_sigset_t *oldset) {
|
||||
// If this signal is blocked, such calls cannot be handled and the process may
|
||||
// hang.
|
||||
internal_sigdelset(&set, 31);
|
||||
|
||||
// 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
|
||||
|
||||
SetSigProcMask(&set, oldset);
|
||||
}
|
||||
|
||||
@ -214,7 +225,7 @@ uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
|
||||
// mmap2 specifies file offset in 4096-byte units.
|
||||
CHECK(IsAligned(offset, 4096));
|
||||
return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
|
||||
offset / 4096);
|
||||
(OFF_T)(offset / 4096));
|
||||
# endif
|
||||
}
|
||||
# endif // !SANITIZER_S390
|
||||
@ -239,9 +250,7 @@ int internal_madvise(uptr addr, uptr length, int advice) {
|
||||
return internal_syscall(SYSCALL(madvise), addr, length, advice);
|
||||
}
|
||||
|
||||
uptr internal_close(fd_t fd) {
|
||||
return internal_syscall(SYSCALL(close), fd);
|
||||
}
|
||||
uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); }
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
# if SANITIZER_LINUX
|
||||
@ -276,12 +285,12 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
|
||||
|
||||
uptr internal_ftruncate(fd_t fd, uptr size) {
|
||||
sptr res;
|
||||
HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
|
||||
(OFF_T)size));
|
||||
HANDLE_EINTR(res,
|
||||
(sptr)internal_syscall(SYSCALL(ftruncate), fd, (OFF_T)size));
|
||||
return res;
|
||||
}
|
||||
|
||||
#if (!SANITIZER_LINUX_USES_64BIT_SYSCALLS || SANITIZER_SPARC) && SANITIZER_LINUX
|
||||
# if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX
|
||||
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
|
||||
internal_memset(out, 0, sizeof(*out));
|
||||
out->st_dev = in->st_dev;
|
||||
@ -322,7 +331,12 @@ static void statx_to_stat(struct statx *in, struct stat *out) {
|
||||
}
|
||||
# endif
|
||||
|
||||
# if SANITIZER_MIPS64 || SANITIZER_SPARC64
|
||||
# if SANITIZER_MIPS64
|
||||
typedef struct kernel_stat kstat_t;
|
||||
# else
|
||||
typedef struct kernel_stat64 kstat_t;
|
||||
# endif
|
||||
// Undefine compatibility macros from <sys/stat.h>
|
||||
// so that they would not clash with the kernel_stat
|
||||
// st_[a|m|c]time fields
|
||||
@ -340,7 +354,7 @@ static void statx_to_stat(struct statx *in, struct stat *out) {
|
||||
# undef st_mtime_nsec
|
||||
# undef st_ctime_nsec
|
||||
# endif
|
||||
static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
|
||||
static void kernel_stat_to_stat(kstat_t *in, struct stat *out) {
|
||||
internal_memset(out, 0, sizeof(*out));
|
||||
out->st_dev = in->st_dev;
|
||||
out->st_ino = in->st_ino;
|
||||
@ -352,8 +366,7 @@ static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
|
||||
out->st_size = in->st_size;
|
||||
out->st_blksize = in->st_blksize;
|
||||
out->st_blocks = in->st_blocks;
|
||||
#if defined(__USE_MISC) || \
|
||||
defined(__USE_XOPEN2K8) || \
|
||||
# if defined(__USE_MISC) || defined(__USE_XOPEN2K8) || \
|
||||
defined(SANITIZER_ANDROID)
|
||||
out->st_atim.tv_sec = in->st_atime;
|
||||
out->st_atim.tv_nsec = in->st_atime_nsec;
|
||||
@ -387,6 +400,12 @@ uptr internal_stat(const char *path, void *buf) {
|
||||
!SANITIZER_SPARC
|
||||
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
|
||||
0);
|
||||
# elif SANITIZER_SPARC64
|
||||
kstat_t buf64;
|
||||
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
|
||||
(uptr)&buf64, 0);
|
||||
kernel_stat_to_stat(&buf64, (struct stat *)buf);
|
||||
return res;
|
||||
# else
|
||||
struct stat64 buf64;
|
||||
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
|
||||
@ -419,6 +438,12 @@ uptr internal_lstat(const char *path, void *buf) {
|
||||
!SANITIZER_SPARC
|
||||
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
|
||||
AT_SYMLINK_NOFOLLOW);
|
||||
# elif SANITIZER_SPARC64
|
||||
kstat_t buf64;
|
||||
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
|
||||
(uptr)&buf64, AT_SYMLINK_NOFOLLOW);
|
||||
kernel_stat_to_stat(&buf64, (struct stat *)buf);
|
||||
return res;
|
||||
# else
|
||||
struct stat64 buf64;
|
||||
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
|
||||
@ -438,10 +463,16 @@ uptr internal_fstat(fd_t fd, void *buf) {
|
||||
# if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
|
||||
# if SANITIZER_MIPS64
|
||||
// For mips64, fstat syscall fills buffer in the format of kernel_stat
|
||||
struct kernel_stat kbuf;
|
||||
kstat_t kbuf;
|
||||
int res = internal_syscall(SYSCALL(fstat), fd, &kbuf);
|
||||
kernel_stat_to_stat(&kbuf, (struct stat *)buf);
|
||||
return res;
|
||||
# elif SANITIZER_LINUX && SANITIZER_SPARC64
|
||||
// For sparc64, fstat64 syscall fills buffer in the format of kernel_stat64
|
||||
kstat_t kbuf;
|
||||
int res = internal_syscall(SYSCALL(fstat64), fd, &kbuf);
|
||||
kernel_stat_to_stat(&kbuf, (struct stat *)buf);
|
||||
return res;
|
||||
# elif SANITIZER_LINUX && defined(__loongarch__)
|
||||
struct statx bufx;
|
||||
int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH,
|
||||
@ -466,9 +497,7 @@ uptr internal_filesize(fd_t fd) {
|
||||
return (uptr)st.st_size;
|
||||
}
|
||||
|
||||
uptr internal_dup(int oldfd) {
|
||||
return internal_syscall(SYSCALL(dup), oldfd);
|
||||
}
|
||||
uptr internal_dup(int oldfd) { return internal_syscall(SYSCALL(dup), oldfd); }
|
||||
|
||||
uptr internal_dup2(int oldfd, int newfd) {
|
||||
# if SANITIZER_LINUX
|
||||
@ -507,9 +536,7 @@ uptr internal_rename(const char *oldpath, const char *newpath) {
|
||||
# endif
|
||||
}
|
||||
|
||||
uptr internal_sched_yield() {
|
||||
return internal_syscall(SYSCALL(sched_yield));
|
||||
}
|
||||
uptr internal_sched_yield() { return internal_syscall(SYSCALL(sched_yield)); }
|
||||
|
||||
void internal_usleep(u64 useconds) {
|
||||
struct timespec ts;
|
||||
@ -574,7 +601,9 @@ int TgKill(pid_t pid, tid_t tid, int sig) {
|
||||
return internal_syscall(SYSCALL(thr_kill2), pid, tid, sig);
|
||||
# elif SANITIZER_SOLARIS
|
||||
(void)pid;
|
||||
return thr_kill(tid, sig);
|
||||
errno = thr_kill(tid, sig);
|
||||
// TgKill is expected to return -1 on error, not an errno.
|
||||
return errno != 0 ? -1 : 0;
|
||||
# endif
|
||||
}
|
||||
# endif
|
||||
@ -621,13 +650,13 @@ const char *GetEnv(const char *name) {
|
||||
if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
|
||||
environ = nullptr;
|
||||
}
|
||||
if (!environ || len == 0) return nullptr;
|
||||
if (!environ || len == 0)
|
||||
return nullptr;
|
||||
uptr namelen = internal_strlen(name);
|
||||
const char *p = environ;
|
||||
while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
|
||||
// proc file has the format NAME=value\0NAME=value\0NAME=value\0...
|
||||
const char* endp =
|
||||
(char*)internal_memchr(p, '\0', len - (p - environ));
|
||||
const char *endp = (char *)internal_memchr(p, '\0', len - (p - environ));
|
||||
if (!endp) // this entry isn't NUL terminated
|
||||
return nullptr;
|
||||
else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
|
||||
@ -661,7 +690,8 @@ static void ReadNullSepFileToArray(const char *path, char ***arr,
|
||||
int count, i;
|
||||
for (count = 1, i = 1;; i++) {
|
||||
if (buff[i] == 0) {
|
||||
if (buff[i+1] == 0) break;
|
||||
if (buff[i + 1] == 0)
|
||||
break;
|
||||
(*arr)[count] = &buff[i + 1];
|
||||
CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.
|
||||
count++;
|
||||
@ -780,13 +810,9 @@ uptr internal_waitpid(int pid, int *status, int options) {
|
||||
0 /* rusage */);
|
||||
}
|
||||
|
||||
uptr internal_getpid() {
|
||||
return internal_syscall(SYSCALL(getpid));
|
||||
}
|
||||
uptr internal_getpid() { return internal_syscall(SYSCALL(getpid)); }
|
||||
|
||||
uptr internal_getppid() {
|
||||
return internal_syscall(SYSCALL(getppid));
|
||||
}
|
||||
uptr internal_getppid() { return internal_syscall(SYSCALL(getppid)); }
|
||||
|
||||
int internal_dlinfo(void *handle, int request, void *p) {
|
||||
# if SANITIZER_FREEBSD
|
||||
@ -827,10 +853,16 @@ uptr internal_sigaltstack(const void *ss, void *oss) {
|
||||
return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
|
||||
}
|
||||
|
||||
extern "C" pid_t __fork(void);
|
||||
|
||||
int internal_fork() {
|
||||
# if SANITIZER_LINUX
|
||||
# if SANITIZER_S390
|
||||
return internal_syscall(SYSCALL(clone), 0, SIGCHLD);
|
||||
# elif SANITIZER_SPARC
|
||||
// The clone syscall interface on SPARC differs massively from the rest,
|
||||
// so fall back to __fork.
|
||||
return __fork();
|
||||
# else
|
||||
return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
|
||||
# endif
|
||||
@ -1082,7 +1114,8 @@ static uptr GetKernelAreaSize() {
|
||||
return 0;
|
||||
MemoryMappedSegment segment;
|
||||
while (proc_maps.Next(&segment)) {
|
||||
if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0;
|
||||
if ((segment.end >= 3 * gbyte) && segment.IsWritable())
|
||||
return 0;
|
||||
}
|
||||
|
||||
# if !SANITIZER_ANDROID
|
||||
@ -1109,7 +1142,8 @@ uptr GetMaxVirtualAddress() {
|
||||
# if SANITIZER_NETBSD && defined(__x86_64__)
|
||||
return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE)
|
||||
# elif SANITIZER_WORDSIZE == 64
|
||||
# if defined(__powerpc64__) || defined(__aarch64__) || defined(__loongarch__)
|
||||
# if defined(__powerpc64__) || defined(__aarch64__) || \
|
||||
defined(__loongarch__) || SANITIZER_RISCV64
|
||||
// On PowerPC64 we have two different address space layouts: 44- and 46-bit.
|
||||
// We somehow need to figure out which one we are using now and choose
|
||||
// one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
|
||||
@ -1118,9 +1152,8 @@ uptr GetMaxVirtualAddress() {
|
||||
// This should (does) work for both PowerPC64 Endian modes.
|
||||
// Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
|
||||
// loongarch64 also has multiple address space layouts: default is 47-bit.
|
||||
// RISC-V 64 also has multiple address space layouts: 39, 48 and 57-bit.
|
||||
return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
|
||||
#elif SANITIZER_RISCV64
|
||||
return (1ULL << 38) - 1;
|
||||
# elif SANITIZER_MIPS64
|
||||
return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
|
||||
# elif defined(__s390x__)
|
||||
@ -1149,7 +1182,7 @@ uptr GetMaxUserVirtualAddress() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
# if !SANITIZER_ANDROID || defined(__aarch64__)
|
||||
uptr GetPageSize() {
|
||||
# if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) && \
|
||||
defined(EXEC_PAGESIZE)
|
||||
@ -1168,7 +1201,7 @@ uptr GetPageSize() {
|
||||
return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.
|
||||
# endif
|
||||
}
|
||||
#endif // !SANITIZER_ANDROID
|
||||
# endif
|
||||
|
||||
uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {
|
||||
# if SANITIZER_SOLARIS
|
||||
@ -1190,17 +1223,18 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
|
||||
uptr module_name_len = Size;
|
||||
# else
|
||||
const char *default_module_name = "/proc/self/exe";
|
||||
uptr module_name_len = internal_readlink(
|
||||
default_module_name, buf, buf_len);
|
||||
uptr module_name_len = internal_readlink(default_module_name, buf, buf_len);
|
||||
int readlink_error;
|
||||
bool IsErr = internal_iserror(module_name_len, &readlink_error);
|
||||
#endif // SANITIZER_SOLARIS
|
||||
# endif
|
||||
if (IsErr) {
|
||||
// We can't read binary name for some reason, assume it's unknown.
|
||||
Report("WARNING: reading executable name failed with errno %d, "
|
||||
"some stack frames may not be symbolized\n", readlink_error);
|
||||
module_name_len = internal_snprintf(buf, buf_len, "%s",
|
||||
default_module_name);
|
||||
Report(
|
||||
"WARNING: reading executable name failed with errno %d, "
|
||||
"some stack frames may not be symbolized\n",
|
||||
readlink_error);
|
||||
module_name_len =
|
||||
internal_snprintf(buf, buf_len, "%s", default_module_name);
|
||||
CHECK_LT(module_name_len, buf_len);
|
||||
}
|
||||
return module_name_len;
|
||||
@ -1228,9 +1262,11 @@ bool LibraryNameIs(const char *full_name, const char *base_name) {
|
||||
// Strip path.
|
||||
while (*name != '\0') name++;
|
||||
while (name > full_name && *name != '/') name--;
|
||||
if (*name == '/') name++;
|
||||
if (*name == '/')
|
||||
name++;
|
||||
uptr base_name_length = internal_strlen(base_name);
|
||||
if (internal_strncmp(name, base_name, base_name_length)) return false;
|
||||
if (internal_strncmp(name, base_name, base_name_length))
|
||||
return false;
|
||||
return (name[base_name_length] == '-' || name[base_name_length] == '.');
|
||||
}
|
||||
|
||||
@ -1329,12 +1365,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
/* Return to parent. */
|
||||
"1:\n"
|
||||
: "=a"(res)
|
||||
: "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
|
||||
"S"(child_stack),
|
||||
"D"(flags),
|
||||
"d"(parent_tidptr),
|
||||
"r"(r8),
|
||||
"r"(r10)
|
||||
: "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "S"(child_stack), "D"(flags),
|
||||
"d"(parent_tidptr), "r"(r8), "r"(r10)
|
||||
: "memory", "r11", "rcx");
|
||||
return res;
|
||||
}
|
||||
@ -1404,13 +1436,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
/* Return to parent. */
|
||||
"1:\n"
|
||||
: "=r"(res)
|
||||
: "r"(flags),
|
||||
"r"(child_stack),
|
||||
"r"(parent_tidptr),
|
||||
"r"(a3),
|
||||
"r"(a4),
|
||||
"i"(__NR_clone),
|
||||
"i"(__NR_exit)
|
||||
: "r"(flags), "r"(child_stack), "r"(parent_tidptr), "r"(a3), "r"(a4),
|
||||
"i"(__NR_clone), "i"(__NR_exit)
|
||||
: "memory", "$29");
|
||||
return res;
|
||||
}
|
||||
@ -1499,10 +1526,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
"1:\n"
|
||||
|
||||
: "=r"(res)
|
||||
: "i"(-EINVAL),
|
||||
"r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
|
||||
"r"(__ptid), "r"(__tls), "r"(__ctid),
|
||||
"i"(__NR_clone), "i"(__NR_exit)
|
||||
: "i"(-EINVAL), "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
|
||||
"r"(__ptid), "r"(__tls), "r"(__ctid), "i"(__NR_clone), "i"(__NR_exit)
|
||||
: "x30", "memory");
|
||||
return res;
|
||||
}
|
||||
@ -1544,7 +1569,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
: "=r"(res)
|
||||
: "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__ctid), "r"(__tls),
|
||||
"r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit)
|
||||
: "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8");
|
||||
: "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
|
||||
"$t8");
|
||||
return res;
|
||||
}
|
||||
# elif defined(__powerpc64__)
|
||||
@ -1640,19 +1666,9 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
"1:\n\t"
|
||||
"mr %0, 3\n\t"
|
||||
: "=r"(res)
|
||||
: "0" (-1),
|
||||
"i" (EINVAL),
|
||||
"i" (__NR_clone),
|
||||
"i" (__NR_exit),
|
||||
"r" (__fn),
|
||||
"r" (__cstack),
|
||||
"r" (__flags),
|
||||
"r" (__arg),
|
||||
"r" (__ptidptr),
|
||||
"r" (__newtls),
|
||||
"r" (__ctidptr),
|
||||
"i" (FRAME_SIZE),
|
||||
"i" (FRAME_TOC_SAVE_OFFSET)
|
||||
: "0"(-1), "i"(EINVAL), "i"(__NR_clone), "i"(__NR_exit), "r"(__fn),
|
||||
"r"(__cstack), "r"(__flags), "r"(__arg), "r"(__ptidptr), "r"(__newtls),
|
||||
"r"(__ctidptr), "i"(FRAME_SIZE), "i"(FRAME_TOC_SAVE_OFFSET)
|
||||
: "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
|
||||
return res;
|
||||
}
|
||||
@ -1713,11 +1729,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
"int $0x80\n"
|
||||
"1:\n"
|
||||
: "=a"(res)
|
||||
: "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
|
||||
"c"(child_stack),
|
||||
"d"(parent_tidptr),
|
||||
"S"(newtls),
|
||||
"D"(child_tidptr)
|
||||
: "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "c"(child_stack),
|
||||
"d"(parent_tidptr), "S"(newtls), "D"(child_tidptr)
|
||||
: "memory");
|
||||
return res;
|
||||
}
|
||||
@ -1774,16 +1787,14 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
|
||||
/* In the child, now. Call "fn(arg)". */
|
||||
"ldr r0, [sp, #4]\n"
|
||||
"ldr ip, [sp], #8\n"
|
||||
BLX(ip)
|
||||
"ldr ip, [sp], #8\n" BLX(ip)
|
||||
/* Call _exit(%r0). */
|
||||
"mov r7, %7\n"
|
||||
"swi 0x0\n"
|
||||
"1:\n"
|
||||
"mov %0, r0\n"
|
||||
: "=r"(res)
|
||||
: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
|
||||
"i"(__NR_exit)
|
||||
: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7), "i"(__NR_exit)
|
||||
: "memory");
|
||||
return res;
|
||||
}
|
||||
@ -1843,7 +1854,8 @@ extern "C" __attribute__((weak)) void* _DYNAMIC;
|
||||
AndroidApiLevel AndroidGetApiLevel() {
|
||||
AndroidApiLevel level =
|
||||
(AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
|
||||
if (level) return level;
|
||||
if (level)
|
||||
return level;
|
||||
level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic()
|
||||
: AndroidDetectApiLevel();
|
||||
atomic_store(&android_api_level, level, memory_order_relaxed);
|
||||
@ -1879,18 +1891,18 @@ HandleSignalMode GetHandleSignalMode(int signum) {
|
||||
|
||||
# if !SANITIZER_GO
|
||||
void *internal_start_thread(void *(*func)(void *arg), void *arg) {
|
||||
if (&real_pthread_create == 0)
|
||||
if (&internal_pthread_create == 0)
|
||||
return nullptr;
|
||||
// Start the thread with signals blocked, otherwise it can steal user signals.
|
||||
ScopedBlockSignals block(nullptr);
|
||||
void *th;
|
||||
real_pthread_create(&th, nullptr, func, arg);
|
||||
internal_pthread_create(&th, nullptr, func, arg);
|
||||
return th;
|
||||
}
|
||||
|
||||
void internal_join_thread(void *th) {
|
||||
if (&real_pthread_join)
|
||||
real_pthread_join(th, nullptr);
|
||||
if (&internal_pthread_join)
|
||||
internal_pthread_join(th, nullptr);
|
||||
}
|
||||
# else
|
||||
void *internal_start_thread(void *(*func)(void *), void *arg) { return 0; }
|
||||
@ -1910,7 +1922,8 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
|
||||
u8 *aux = reinterpret_cast<u8 *>(ucontext->uc_mcontext.__reserved);
|
||||
while (true) {
|
||||
_aarch64_ctx *ctx = (_aarch64_ctx *)aux;
|
||||
if (ctx->size == 0) break;
|
||||
if (ctx->size == 0)
|
||||
break;
|
||||
if (ctx->magic == kEsrMagic) {
|
||||
*esr = ((__sanitizer_esr_context *)ctx)->esr;
|
||||
return true;
|
||||
@ -1921,9 +1934,7 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
|
||||
}
|
||||
# elif SANITIZER_FREEBSD && defined(__aarch64__)
|
||||
// FreeBSD doesn't provide ESR in the ucontext.
|
||||
static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
|
||||
return false;
|
||||
}
|
||||
static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { return false; }
|
||||
# endif
|
||||
|
||||
using Context = ucontext_t;
|
||||
@ -1999,7 +2010,8 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
|
||||
# elif defined(__aarch64__)
|
||||
static const u64 ESR_ELx_WNR = 1U << 6;
|
||||
u64 esr;
|
||||
if (!Aarch64GetESR(ucontext, &esr)) return Unknown;
|
||||
if (!Aarch64GetESR(ucontext, &esr))
|
||||
return Unknown;
|
||||
return esr & ESR_ELx_WNR ? Write : Read;
|
||||
# elif defined(__loongarch__)
|
||||
u32 flags = ucontext->uc_mcontext.__flags;
|
||||
@ -2139,8 +2151,167 @@ bool SignalContext::IsTrueFaultingAddress() const {
|
||||
return si->si_signo == SIGSEGV && si->si_code != 128;
|
||||
}
|
||||
|
||||
UNUSED
|
||||
static const char *RegNumToRegName(int reg) {
|
||||
switch (reg) {
|
||||
# if SANITIZER_LINUX
|
||||
# if defined(__x86_64__)
|
||||
case REG_RAX:
|
||||
return "rax";
|
||||
case REG_RBX:
|
||||
return "rbx";
|
||||
case REG_RCX:
|
||||
return "rcx";
|
||||
case REG_RDX:
|
||||
return "rdx";
|
||||
case REG_RDI:
|
||||
return "rdi";
|
||||
case REG_RSI:
|
||||
return "rsi";
|
||||
case REG_RBP:
|
||||
return "rbp";
|
||||
case REG_RSP:
|
||||
return "rsp";
|
||||
case REG_R8:
|
||||
return "r8";
|
||||
case REG_R9:
|
||||
return "r9";
|
||||
case REG_R10:
|
||||
return "r10";
|
||||
case REG_R11:
|
||||
return "r11";
|
||||
case REG_R12:
|
||||
return "r12";
|
||||
case REG_R13:
|
||||
return "r13";
|
||||
case REG_R14:
|
||||
return "r14";
|
||||
case REG_R15:
|
||||
return "r15";
|
||||
# elif defined(__i386__)
|
||||
case REG_EAX:
|
||||
return "eax";
|
||||
case REG_EBX:
|
||||
return "ebx";
|
||||
case REG_ECX:
|
||||
return "ecx";
|
||||
case REG_EDX:
|
||||
return "edx";
|
||||
case REG_EDI:
|
||||
return "edi";
|
||||
case REG_ESI:
|
||||
return "esi";
|
||||
case REG_EBP:
|
||||
return "ebp";
|
||||
case REG_ESP:
|
||||
return "esp";
|
||||
# endif
|
||||
# endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
# if SANITIZER_LINUX
|
||||
UNUSED
|
||||
static void DumpSingleReg(ucontext_t *ctx, int RegNum) {
|
||||
const char *RegName = RegNumToRegName(RegNum);
|
||||
# if defined(__x86_64__)
|
||||
Printf("%s%s = 0x%016llx ", internal_strlen(RegName) == 2 ? " " : "",
|
||||
RegName, ctx->uc_mcontext.gregs[RegNum]);
|
||||
# elif defined(__i386__)
|
||||
Printf("%s = 0x%08x ", RegName, ctx->uc_mcontext.gregs[RegNum]);
|
||||
# else
|
||||
(void)RegName;
|
||||
# endif
|
||||
}
|
||||
# endif
|
||||
|
||||
void SignalContext::DumpAllRegisters(void *context) {
|
||||
// FIXME: Implement this.
|
||||
ucontext_t *ucontext = (ucontext_t *)context;
|
||||
# if SANITIZER_LINUX
|
||||
# if defined(__x86_64__)
|
||||
Report("Register values:\n");
|
||||
DumpSingleReg(ucontext, REG_RAX);
|
||||
DumpSingleReg(ucontext, REG_RBX);
|
||||
DumpSingleReg(ucontext, REG_RCX);
|
||||
DumpSingleReg(ucontext, REG_RDX);
|
||||
Printf("\n");
|
||||
DumpSingleReg(ucontext, REG_RDI);
|
||||
DumpSingleReg(ucontext, REG_RSI);
|
||||
DumpSingleReg(ucontext, REG_RBP);
|
||||
DumpSingleReg(ucontext, REG_RSP);
|
||||
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(__i386__)
|
||||
// Duplication of this report print is caused by partial support
|
||||
// of register values dumping. In case of unsupported yet architecture let's
|
||||
// avoid printing 'Register values:' without actual values in the following
|
||||
// output.
|
||||
Report("Register values:\n");
|
||||
DumpSingleReg(ucontext, REG_EAX);
|
||||
DumpSingleReg(ucontext, REG_EBX);
|
||||
DumpSingleReg(ucontext, REG_ECX);
|
||||
DumpSingleReg(ucontext, REG_EDX);
|
||||
Printf("\n");
|
||||
DumpSingleReg(ucontext, REG_EDI);
|
||||
DumpSingleReg(ucontext, REG_ESI);
|
||||
DumpSingleReg(ucontext, REG_EBP);
|
||||
DumpSingleReg(ucontext, REG_ESP);
|
||||
Printf("\n");
|
||||
# else
|
||||
(void)ucontext;
|
||||
# endif
|
||||
# elif SANITIZER_FREEBSD
|
||||
# if defined(__x86_64__)
|
||||
Report("Register values:\n");
|
||||
Printf("rax = 0x%016lx ", ucontext->uc_mcontext.mc_rax);
|
||||
Printf("rbx = 0x%016lx ", ucontext->uc_mcontext.mc_rbx);
|
||||
Printf("rcx = 0x%016lx ", ucontext->uc_mcontext.mc_rcx);
|
||||
Printf("rdx = 0x%016lx ", ucontext->uc_mcontext.mc_rdx);
|
||||
Printf("\n");
|
||||
Printf("rdi = 0x%016lx ", ucontext->uc_mcontext.mc_rdi);
|
||||
Printf("rsi = 0x%016lx ", ucontext->uc_mcontext.mc_rsi);
|
||||
Printf("rbp = 0x%016lx ", ucontext->uc_mcontext.mc_rbp);
|
||||
Printf("rsp = 0x%016lx ", ucontext->uc_mcontext.mc_rsp);
|
||||
Printf("\n");
|
||||
Printf(" r8 = 0x%016lx ", ucontext->uc_mcontext.mc_r8);
|
||||
Printf(" r9 = 0x%016lx ", ucontext->uc_mcontext.mc_r9);
|
||||
Printf("r10 = 0x%016lx ", ucontext->uc_mcontext.mc_r10);
|
||||
Printf("r11 = 0x%016lx ", ucontext->uc_mcontext.mc_r11);
|
||||
Printf("\n");
|
||||
Printf("r12 = 0x%016lx ", ucontext->uc_mcontext.mc_r12);
|
||||
Printf("r13 = 0x%016lx ", ucontext->uc_mcontext.mc_r13);
|
||||
Printf("r14 = 0x%016lx ", ucontext->uc_mcontext.mc_r14);
|
||||
Printf("r15 = 0x%016lx ", ucontext->uc_mcontext.mc_r15);
|
||||
Printf("\n");
|
||||
# elif defined(__i386__)
|
||||
Report("Register values:\n");
|
||||
Printf("eax = 0x%08x ", ucontext->uc_mcontext.mc_eax);
|
||||
Printf("ebx = 0x%08x ", ucontext->uc_mcontext.mc_ebx);
|
||||
Printf("ecx = 0x%08x ", ucontext->uc_mcontext.mc_ecx);
|
||||
Printf("edx = 0x%08x ", ucontext->uc_mcontext.mc_edx);
|
||||
Printf("\n");
|
||||
Printf("edi = 0x%08x ", ucontext->uc_mcontext.mc_edi);
|
||||
Printf("esi = 0x%08x ", ucontext->uc_mcontext.mc_esi);
|
||||
Printf("ebp = 0x%08x ", ucontext->uc_mcontext.mc_ebp);
|
||||
Printf("esp = 0x%08x ", ucontext->uc_mcontext.mc_esp);
|
||||
Printf("\n");
|
||||
# else
|
||||
(void)ucontext;
|
||||
# endif
|
||||
# endif
|
||||
// FIXME: Implement this for other OSes and architectures.
|
||||
}
|
||||
|
||||
static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
@ -2308,7 +2479,8 @@ void CheckASLR() {
|
||||
}
|
||||
|
||||
if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_ASLR)) {
|
||||
Printf("This sanitizer is not compatible with enabled ASLR.\n"
|
||||
Printf(
|
||||
"This sanitizer is not compatible with enabled ASLR.\n"
|
||||
"To disable ASLR, please run \"paxctl +a %s\" and try again.\n",
|
||||
GetArgv()[0]);
|
||||
Die();
|
||||
@ -2323,9 +2495,13 @@ void CheckASLR() {
|
||||
return;
|
||||
}
|
||||
if ((aslr_status & PROC_ASLR_ACTIVE) != 0) {
|
||||
Printf("This sanitizer is not compatible with enabled ASLR "
|
||||
"and binaries compiled with PIE\n");
|
||||
Die();
|
||||
VReport(1,
|
||||
"This sanitizer is not compatible with enabled ASLR "
|
||||
"and binaries compiled with PIE\n"
|
||||
"ASLR will be disabled and the program re-executed.\n");
|
||||
int aslr_ctl = PROC_ASLR_FORCE_DISABLE;
|
||||
CHECK_NE(internal_procctl(P_PID, 0, PROC_ASLR_CTL, &aslr_ctl), -1);
|
||||
ReExec();
|
||||
}
|
||||
# elif SANITIZER_PPC64V2
|
||||
// Disable ASLR for Linux PPC64LE.
|
||||
|
||||
42
lib/tsan/sanitizer_common/sanitizer_linux.h
vendored
42
lib/tsan/sanitizer_common/sanitizer_linux.h
vendored
@ -139,29 +139,53 @@ inline void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) {
|
||||
|
||||
# if defined(__aarch64__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mrs %0, tpidr_el0" : "=r"(__v)); __v; })
|
||||
({ \
|
||||
void **__v; \
|
||||
__asm__("mrs %0, tpidr_el0" : "=r"(__v)); \
|
||||
__v; \
|
||||
})
|
||||
# elif defined(__arm__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); __v; })
|
||||
({ \
|
||||
void **__v; \
|
||||
__asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); \
|
||||
__v; \
|
||||
})
|
||||
# elif defined(__mips__)
|
||||
// On mips32r1, this goes via a kernel illegal instruction trap that's
|
||||
// optimized for v1.
|
||||
# define __get_tls() \
|
||||
({ register void** __v asm("v1"); \
|
||||
__asm__(".set push\n" \
|
||||
({ \
|
||||
register void **__v asm("v1"); \
|
||||
__asm__( \
|
||||
".set push\n" \
|
||||
".set mips32r2\n" \
|
||||
"rdhwr %0,$29\n" \
|
||||
".set pop\n" : "=r"(__v)); \
|
||||
__v; })
|
||||
".set pop\n" \
|
||||
: "=r"(__v)); \
|
||||
__v; \
|
||||
})
|
||||
# elif defined(__riscv)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mv %0, tp" : "=r"(__v)); __v; })
|
||||
({ \
|
||||
void **__v; \
|
||||
__asm__("mv %0, tp" : "=r"(__v)); \
|
||||
__v; \
|
||||
})
|
||||
# elif defined(__i386__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("movl %%gs:0, %0" : "=r"(__v)); __v; })
|
||||
({ \
|
||||
void **__v; \
|
||||
__asm__("movl %%gs:0, %0" : "=r"(__v)); \
|
||||
__v; \
|
||||
})
|
||||
# elif defined(__x86_64__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mov %%fs:0, %0" : "=r"(__v)); __v; })
|
||||
({ \
|
||||
void **__v; \
|
||||
__asm__("mov %%fs:0, %0" : "=r"(__v)); \
|
||||
__v; \
|
||||
})
|
||||
# else
|
||||
# error "Unsupported architecture."
|
||||
# endif
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
# include "sanitizer_common.h"
|
||||
# include "sanitizer_file.h"
|
||||
# include "sanitizer_flags.h"
|
||||
#include "sanitizer_freebsd.h"
|
||||
# include "sanitizer_getauxval.h"
|
||||
# include "sanitizer_glibc_version.h"
|
||||
# include "sanitizer_linux.h"
|
||||
@ -47,19 +46,22 @@
|
||||
|
||||
# if SANITIZER_FREEBSD
|
||||
# include <pthread_np.h>
|
||||
#include <osreldate.h>
|
||||
# include <sys/auxv.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define pthread_getattr_np pthread_attr_get_np
|
||||
// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
|
||||
// that, it was never implemented. So just define it to zero.
|
||||
# undef MAP_NORESERVE
|
||||
# define MAP_NORESERVE 0
|
||||
extern const Elf_Auxinfo *__elf_aux_vector;
|
||||
extern "C" int __sys_sigaction(int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_NETBSD
|
||||
# include <lwp.h>
|
||||
# include <sys/sysctl.h>
|
||||
# include <sys/tls.h>
|
||||
#include <lwp.h>
|
||||
# endif
|
||||
|
||||
# if SANITIZER_SOLARIS
|
||||
@ -89,16 +91,26 @@ struct __sanitizer::linux_dirent {
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
SANITIZER_WEAK_ATTRIBUTE int
|
||||
real_sigaction(int signum, const void *act, void *oldact);
|
||||
SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act,
|
||||
void *oldact);
|
||||
|
||||
int internal_sigaction(int signum, const void *act, void *oldact) {
|
||||
# if SANITIZER_FREEBSD
|
||||
// On FreeBSD, call the sigaction syscall directly (part of libsys in FreeBSD
|
||||
// 15) since the libc version goes via a global interposing table. Due to
|
||||
// library initialization order the table can be relocated after the call to
|
||||
// InitializeDeadlySignals() which then crashes when dereferencing the
|
||||
// uninitialized pointer in libc.
|
||||
return __sys_sigaction(signum, (const struct sigaction *)act,
|
||||
(struct sigaction *)oldact);
|
||||
# else
|
||||
# if !SANITIZER_GO
|
||||
if (&real_sigaction)
|
||||
return real_sigaction(signum, act, oldact);
|
||||
# endif
|
||||
return sigaction(signum, (const struct sigaction *)act,
|
||||
(struct sigaction *)oldact);
|
||||
# endif
|
||||
}
|
||||
|
||||
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
||||
@ -119,7 +131,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
||||
MemoryMappedSegment segment;
|
||||
uptr prev_end = 0;
|
||||
while (proc_maps.Next(&segment)) {
|
||||
if ((uptr)&rl < segment.end) break;
|
||||
if ((uptr)&rl < segment.end)
|
||||
break;
|
||||
prev_end = segment.end;
|
||||
}
|
||||
CHECK((uptr)&rl >= segment.start && (uptr)&rl < segment.end);
|
||||
@ -127,7 +140,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
||||
// Get stacksize from rlimit, but clip it so that it does not overlap
|
||||
// with other mappings.
|
||||
uptr stacksize = rl.rlim_cur;
|
||||
if (stacksize > segment.end - prev_end) stacksize = segment.end - prev_end;
|
||||
if (stacksize > segment.end - prev_end)
|
||||
stacksize = segment.end - prev_end;
|
||||
// When running with unlimited stack size, we still want to set some limit.
|
||||
// The unlimited stack size is caused by 'ulimit -s unlimited'.
|
||||
// Also, for some reason, GNU make spawns subprocesses with unlimited stack.
|
||||
@ -135,6 +149,19 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
||||
stacksize = kMaxThreadStackSize;
|
||||
*stack_top = segment.end;
|
||||
*stack_bottom = segment.end - stacksize;
|
||||
|
||||
uptr maxAddr = GetMaxUserVirtualAddress();
|
||||
// Edge case: the stack mapping on some systems may be off-by-one e.g.,
|
||||
// fffffffdf000-1000000000000 rw-p 00000000 00:00 0 [stack]
|
||||
// instead of:
|
||||
// fffffffdf000- ffffffffffff
|
||||
// The out-of-range stack_top can result in an invalid shadow address
|
||||
// calculation, since those usually assume the parameters are in range.
|
||||
if (*stack_top == maxAddr + 1)
|
||||
*stack_top = maxAddr;
|
||||
else
|
||||
CHECK_LE(*stack_top, maxAddr);
|
||||
|
||||
return;
|
||||
}
|
||||
uptr stacksize = 0;
|
||||
@ -205,8 +232,8 @@ void InitTlsSize() {
|
||||
g_use_dlpi_tls_data =
|
||||
GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25;
|
||||
|
||||
#if defined(__aarch64__) || defined(__x86_64__) || defined(__powerpc64__) || \
|
||||
defined(__loongarch__)
|
||||
# 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);
|
||||
@ -391,8 +418,7 @@ static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size,
|
||||
// Call __tls_get_addr as a fallback. This forces TLS allocation on glibc
|
||||
// and FreeBSD.
|
||||
# ifdef __s390__
|
||||
begin = (uptr)__builtin_thread_pointer() +
|
||||
TlsGetOffset(tls_modid, 0);
|
||||
begin = (uptr)__builtin_thread_pointer() + TlsGetOffset(tls_modid, 0);
|
||||
# else
|
||||
size_t mod_and_off[2] = {tls_modid, 0};
|
||||
begin = (uptr)__tls_get_addr(mod_and_off);
|
||||
@ -453,9 +479,7 @@ static struct tls_tcb * ThreadSelfTlsTcb() {
|
||||
return tcb;
|
||||
}
|
||||
|
||||
uptr ThreadSelf() {
|
||||
return (uptr)ThreadSelfTlsTcb()->tcb_pthread;
|
||||
}
|
||||
uptr ThreadSelf() { return (uptr)ThreadSelfTlsTcb()->tcb_pthread; }
|
||||
|
||||
int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) {
|
||||
const Elf_Phdr *hdr = info->dlpi_phdr;
|
||||
@ -628,11 +652,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
|
||||
# if !SANITIZER_FREEBSD
|
||||
typedef ElfW(Phdr) Elf_Phdr;
|
||||
#elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
|
||||
#define Elf_Phdr XElf32_Phdr
|
||||
#define dl_phdr_info xdl_phdr_info
|
||||
#define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
|
||||
#endif // !SANITIZER_FREEBSD
|
||||
# endif
|
||||
|
||||
struct DlIteratePhdrData {
|
||||
InternalMmapVectorNoCtor<LoadedModule> *modules;
|
||||
@ -652,8 +672,7 @@ static int AddModuleSegments(const char *module_name, dl_phdr_info *info,
|
||||
uptr cur_end = cur_beg + phdr->p_memsz;
|
||||
bool executable = phdr->p_flags & PF_X;
|
||||
bool writable = phdr->p_flags & PF_W;
|
||||
cur_module.addAddressRange(cur_beg, cur_end, executable,
|
||||
writable);
|
||||
cur_module.addAddressRange(cur_beg, cur_end, executable, writable);
|
||||
} else if (phdr->p_type == PT_NOTE) {
|
||||
# ifdef NT_GNU_BUILD_ID
|
||||
uptr off = 0;
|
||||
@ -698,11 +717,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
|
||||
return AddModuleSegments(module_name.data(), info, data->modules);
|
||||
}
|
||||
|
||||
if (info->dlpi_name) {
|
||||
InternalScopedString module_name;
|
||||
module_name.append("%s", info->dlpi_name);
|
||||
return AddModuleSegments(module_name.data(), info, data->modules);
|
||||
}
|
||||
if (info->dlpi_name)
|
||||
return AddModuleSegments(info->dlpi_name, info, data->modules);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -776,15 +792,12 @@ uptr GetRSS() {
|
||||
// We need the second number which is RSS in pages.
|
||||
char *pos = buf;
|
||||
// Skip the first number.
|
||||
while (*pos >= '0' && *pos <= '9')
|
||||
pos++;
|
||||
while (*pos >= '0' && *pos <= '9') pos++;
|
||||
// Skip whitespaces.
|
||||
while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
|
||||
pos++;
|
||||
while (!(*pos >= '0' && *pos <= '9') && *pos != 0) pos++;
|
||||
// Read the number.
|
||||
uptr rss = 0;
|
||||
while (*pos >= '0' && *pos <= '9')
|
||||
rss = rss * 10 + *pos++ - '0';
|
||||
while (*pos >= '0' && *pos <= '9') rss = rss * 10 + *pos++ - '0';
|
||||
return rss * GetPageSizeCached();
|
||||
}
|
||||
|
||||
@ -827,8 +840,8 @@ u32 GetNumberOfCPUs() {
|
||||
break;
|
||||
if (entry->d_ino != 0 && *d_type == DT_DIR) {
|
||||
if (entry->d_name[0] == 'c' && entry->d_name[1] == 'p' &&
|
||||
entry->d_name[2] == 'u' &&
|
||||
entry->d_name[3] >= '0' && entry->d_name[3] <= '9')
|
||||
entry->d_name[2] == 'u' && entry->d_name[3] >= '0' &&
|
||||
entry->d_name[3] <= '9')
|
||||
n_cpus++;
|
||||
}
|
||||
entry = (struct linux_dirent *)(((u8 *)entry) + entry->d_reclen);
|
||||
@ -858,10 +871,12 @@ static bool ShouldLogAfterPrintf() {
|
||||
return atomic_load(&android_log_initialized, memory_order_acquire);
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE
|
||||
int async_safe_write_log(int pri, const char* tag, const char* msg);
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE
|
||||
int __android_log_write(int prio, const char* tag, const char* msg);
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE int async_safe_write_log(int pri,
|
||||
const char *tag,
|
||||
const char *msg);
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE int __android_log_write(int prio,
|
||||
const char *tag,
|
||||
const char *msg);
|
||||
|
||||
// ANDROID_LOG_INFO is 4, but can't be resolved at runtime.
|
||||
# define SANITIZER_ANDROID_LOG_INFO 4
|
||||
@ -883,8 +898,8 @@ void WriteOneLineToSyslog(const char *s) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE
|
||||
void android_set_abort_message(const char *);
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE void android_set_abort_message(
|
||||
const char *);
|
||||
|
||||
void SetAbortMessage(const char *str) {
|
||||
if (&android_set_abort_message)
|
||||
@ -918,8 +933,8 @@ inline bool CanUseVDSO() { return &__progname && __progname && *__progname; }
|
||||
// MonotonicNanoTime is a timing function that can leverage the vDSO by calling
|
||||
// clock_gettime. real_clock_gettime only exists if clock_gettime is
|
||||
// intercepted, so define it weakly and use it if available.
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE
|
||||
int real_clock_gettime(u32 clk_id, void *tp);
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE int real_clock_gettime(u32 clk_id,
|
||||
void *tp);
|
||||
u64 MonotonicNanoTime() {
|
||||
timespec ts;
|
||||
if (CanUseVDSO()) {
|
||||
@ -944,7 +959,14 @@ u64 MonotonicNanoTime() {
|
||||
void ReExec() {
|
||||
const char *pathname = "/proc/self/exe";
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
# if SANITIZER_FREEBSD
|
||||
for (const auto *aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
|
||||
if (aux->a_type == AT_EXECPATH) {
|
||||
pathname = static_cast<const char *>(aux->a_un.a_ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
# elif SANITIZER_NETBSD
|
||||
static const int name[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC_ARGS,
|
||||
@ -986,9 +1008,8 @@ void UnmapFromTo(uptr from, uptr to) {
|
||||
}
|
||||
|
||||
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
|
||||
uptr min_shadow_base_alignment,
|
||||
UNUSED uptr &high_mem_end) {
|
||||
const uptr granularity = GetMmapGranularity();
|
||||
uptr min_shadow_base_alignment, UNUSED uptr &high_mem_end,
|
||||
uptr granularity) {
|
||||
const uptr alignment =
|
||||
Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
|
||||
const uptr left_padding =
|
||||
|
||||
@ -37,11 +37,8 @@ uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
|
||||
unsigned long fd;
|
||||
unsigned long offset;
|
||||
} params = {
|
||||
(unsigned long)addr,
|
||||
(unsigned long)length,
|
||||
(unsigned long)prot,
|
||||
(unsigned long)flags,
|
||||
(unsigned long)fd,
|
||||
(unsigned long)addr, (unsigned long)length, (unsigned long)prot,
|
||||
(unsigned long)flags, (unsigned long)fd,
|
||||
# ifdef __s390x__
|
||||
(unsigned long)offset,
|
||||
# else
|
||||
@ -108,12 +105,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
/* Return to parent. */
|
||||
"1:\n"
|
||||
: "=r"(res)
|
||||
: "i"(__NR_clone), "i"(__NR_exit),
|
||||
"r"(__cstack),
|
||||
"r"(__flags),
|
||||
"r"(__ptidptr),
|
||||
"r"(__ctidptr),
|
||||
"r"(__newtls)
|
||||
: "i"(__NR_clone), "i"(__NR_exit), "r"(__cstack), "r"(__flags),
|
||||
"r"(__ptidptr), "r"(__ctidptr), "r"(__newtls)
|
||||
: "memory", "cc");
|
||||
if (res >= (uptr)-4095) {
|
||||
errno = -res;
|
||||
@ -211,7 +204,8 @@ void AvoidCVE_2016_2143() {
|
||||
if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
|
||||
return;
|
||||
Report(
|
||||
"ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n"
|
||||
"ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using "
|
||||
"ASan,\n"
|
||||
"MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
|
||||
"machine, or worse.\n"
|
||||
"\n"
|
||||
|
||||
8
lib/tsan/sanitizer_common/sanitizer_mac.cpp
vendored
8
lib/tsan/sanitizer_common/sanitizer_mac.cpp
vendored
@ -1188,8 +1188,8 @@ uptr GetMaxVirtualAddress() {
|
||||
}
|
||||
|
||||
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
|
||||
uptr min_shadow_base_alignment, uptr &high_mem_end) {
|
||||
const uptr granularity = GetMmapGranularity();
|
||||
uptr min_shadow_base_alignment, uptr &high_mem_end,
|
||||
uptr granularity) {
|
||||
const uptr alignment =
|
||||
Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
|
||||
const uptr left_padding =
|
||||
@ -1372,8 +1372,8 @@ void DumpProcessMap() {
|
||||
for (uptr i = 0; i < modules.size(); ++i) {
|
||||
char uuid_str[128];
|
||||
FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
|
||||
Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
|
||||
modules[i].max_address(), modules[i].full_name(),
|
||||
Printf("%p-%p %s (%s) %s\n", (void *)modules[i].base_address(),
|
||||
(void *)modules[i].max_address(), modules[i].full_name(),
|
||||
ModuleArchToString(modules[i].arch()), uuid_str);
|
||||
}
|
||||
Printf("End of module map.\n");
|
||||
|
||||
@ -31,6 +31,10 @@ struct __sanitizer_struct_mallinfo {
|
||||
int v[10];
|
||||
};
|
||||
|
||||
struct __sanitizer_struct_mallinfo2 {
|
||||
uptr v[10];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
@ -123,7 +123,7 @@ INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
InternalScopedString new_name;
|
||||
if (name && zone->introspect == sanitizer_zone.introspect) {
|
||||
new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
|
||||
new_name.AppendF(COMMON_MALLOC_ZONE_NAME "-%s", name);
|
||||
name = new_name.data();
|
||||
}
|
||||
|
||||
|
||||
@ -212,8 +212,10 @@ struct InternalDeadlockDetector {
|
||||
return initialized > 0;
|
||||
}
|
||||
};
|
||||
|
||||
static THREADLOCAL InternalDeadlockDetector deadlock_detector;
|
||||
// This variable is used by the __tls_get_addr interceptor, so cannot use the
|
||||
// global-dynamic TLS model, as that would result in crashes.
|
||||
__attribute__((tls_model("initial-exec"))) static THREADLOCAL
|
||||
InternalDeadlockDetector deadlock_detector;
|
||||
|
||||
void CheckedMutex::LockImpl(uptr pc) { deadlock_detector.Lock(type_, pc); }
|
||||
|
||||
|
||||
@ -17,8 +17,6 @@
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) {
|
||||
return p;
|
||||
}
|
||||
inline void *operator new(__sanitizer::usize sz, void *p) { return p; }
|
||||
|
||||
#endif // SANITIZER_PLACEMENT_NEW_H
|
||||
|
||||
24
lib/tsan/sanitizer_common/sanitizer_platform.h
vendored
24
lib/tsan/sanitizer_common/sanitizer_platform.h
vendored
@ -260,6 +260,17 @@
|
||||
# define SANITIZER_ARM64 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_WINDOWS64 && SANITIZER_ARM64
|
||||
# define SANITIZER_WINDOWS_ARM64 1
|
||||
# define SANITIZER_WINDOWS_x64 0
|
||||
#elif SANITIZER_WINDOWS64 && !SANITIZER_ARM64
|
||||
# define SANITIZER_WINDOWS_ARM64 0
|
||||
# define SANITIZER_WINDOWS_x64 1
|
||||
#else
|
||||
# define SANITIZER_WINDOWS_ARM64 0
|
||||
# define SANITIZER_WINDOWS_x64 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_SOLARIS && SANITIZER_WORDSIZE == 32
|
||||
# define SANITIZER_SOLARIS32 1
|
||||
#else
|
||||
@ -284,7 +295,8 @@
|
||||
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
|
||||
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
|
||||
#ifndef SANITIZER_CAN_USE_ALLOCATOR64
|
||||
# if SANITIZER_RISCV64 || SANITIZER_IOS
|
||||
# if (SANITIZER_RISCV64 && !SANITIZER_FUCHSIA && !SANITIZER_LINUX) || \
|
||||
SANITIZER_IOS || SANITIZER_DRIVERKIT
|
||||
# define SANITIZER_CAN_USE_ALLOCATOR64 0
|
||||
# elif defined(__mips64) || defined(__hexagon__)
|
||||
# define SANITIZER_CAN_USE_ALLOCATOR64 0
|
||||
@ -303,7 +315,15 @@
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
|
||||
# endif
|
||||
#elif SANITIZER_RISCV64
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 38)
|
||||
// FIXME: Rather than hardcoding the VMA here, we should rely on
|
||||
// GetMaxUserVirtualAddress(). This will require some refactoring though since
|
||||
// many places either hardcode some value or SANITIZER_MMAP_RANGE_SIZE is
|
||||
// assumed to be some constant integer.
|
||||
# if SANITIZER_FUCHSIA
|
||||
# define SANITIZER_MMAP_RANGE_SIZE (1ULL << 38)
|
||||
# else
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 56)
|
||||
# endif
|
||||
#elif defined(__aarch64__)
|
||||
# if SANITIZER_APPLE
|
||||
# if SANITIZER_OSX || SANITIZER_IOSSIM
|
||||
|
||||
@ -191,7 +191,8 @@
|
||||
|
||||
#define SANITIZER_INTERCEPT_PREADV \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PWRITEV \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_PREADV64 SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_PWRITEV64 SI_GLIBC
|
||||
|
||||
@ -301,7 +302,8 @@
|
||||
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME (SI_GLIBC || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CONFSTR \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY \
|
||||
(SI_LINUX_NOT_ANDROID || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_STRERROR SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_STRERROR_R SI_POSIX
|
||||
@ -462,7 +464,7 @@
|
||||
(SI_LINUX || SI_MAC || SI_WINDOWS || SI_FREEBSD || SI_NETBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_RECV_RECVFROM SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SEND_SENDTO SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE (SI_LINUX || SI_FREEBSD)
|
||||
|
||||
#define SI_STAT_LINUX (SI_LINUX && __GLIBC_PREREQ(2, 33))
|
||||
#define SANITIZER_INTERCEPT_STAT \
|
||||
@ -575,12 +577,12 @@
|
||||
#define SANITIZER_INTERCEPT_SL_INIT (SI_FREEBSD || SI_NETBSD)
|
||||
|
||||
#define SANITIZER_INTERCEPT_GETRANDOM \
|
||||
((SI_LINUX && __GLIBC_PREREQ(2, 25)) || SI_FREEBSD)
|
||||
((SI_LINUX && __GLIBC_PREREQ(2, 25)) || SI_FREEBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT___CXA_ATEXIT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_ATEXIT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATFORK SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_GETENTROPY \
|
||||
((SI_LINUX && __GLIBC_PREREQ(2, 25)) || SI_FREEBSD)
|
||||
((SI_LINUX && __GLIBC_PREREQ(2, 25)) || SI_FREEBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_QSORT \
|
||||
(SI_POSIX && !SI_IOSSIM && !SI_WATCHOS && !SI_TVOS && !SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_QSORT_R SI_GLIBC
|
||||
@ -594,9 +596,11 @@
|
||||
#define SANITIZER_INTERCEPT___XUNAME SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_FLOPEN SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_PROCCTL SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_HEXDUMP SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_ARGP_PARSE SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_CPUSET_GETAFFINITY SI_FREEBSD
|
||||
// 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))
|
||||
|
||||
// This macro gives a way for downstream users to override the above
|
||||
// interceptor macros irrespective of the platform they are on. They have
|
||||
|
||||
@ -475,6 +475,8 @@ CHECK_TYPE_SIZE(nfds_t);
|
||||
CHECK_TYPE_SIZE(sigset_t);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
|
||||
COMPILER_CHECK(sizeof(__sanitizer_siginfo) == sizeof(siginfo_t));
|
||||
CHECK_SIZE_AND_OFFSET(siginfo_t, si_value);
|
||||
// Can't write checks for sa_handler and sa_sigaction due to them being
|
||||
// preprocessor macros.
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
|
||||
|
||||
@ -301,11 +301,29 @@ struct __sanitizer_sigset_t {
|
||||
|
||||
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
|
||||
|
||||
struct __sanitizer_siginfo {
|
||||
// The size is determined by looking at sizeof of real siginfo_t on linux.
|
||||
u64 opaque[128 / sizeof(u64)];
|
||||
union __sanitizer_sigval {
|
||||
int sival_int;
|
||||
void *sival_ptr;
|
||||
};
|
||||
|
||||
struct __sanitizer_siginfo {
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
pid_t si_pid;
|
||||
u32 si_uid;
|
||||
int si_status;
|
||||
void *si_addr;
|
||||
union __sanitizer_sigval si_value;
|
||||
# if SANITIZER_WORDSIZE == 64
|
||||
char data[40];
|
||||
# else
|
||||
char data[32];
|
||||
# endif
|
||||
};
|
||||
|
||||
typedef __sanitizer_siginfo __sanitizer_siginfo_t;
|
||||
|
||||
using __sanitizer_sighandler_ptr = void (*)(int sig);
|
||||
using __sanitizer_sigactionhandler_ptr = void (*)(int sig,
|
||||
__sanitizer_siginfo *siginfo,
|
||||
@ -726,6 +744,8 @@ struct __sanitizer_cpuset {
|
||||
|
||||
typedef struct __sanitizer_cpuset __sanitizer_cpuset_t;
|
||||
extern unsigned struct_cpuset_sz;
|
||||
|
||||
typedef unsigned long long __sanitizer_eventfd_t;
|
||||
} // namespace __sanitizer
|
||||
|
||||
# define CHECK_TYPE_SIZE(TYPE) \
|
||||
|
||||
@ -523,6 +523,7 @@ typedef long __sanitizer_clock_t;
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
typedef int __sanitizer_clockid_t;
|
||||
typedef unsigned long long __sanitizer_eventfd_t;
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
|
||||
@ -54,12 +54,12 @@ void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
|
||||
return (void *)res;
|
||||
}
|
||||
|
||||
void UnmapOrDie(void *addr, uptr size) {
|
||||
void UnmapOrDie(void *addr, uptr size, bool raw_report) {
|
||||
if (!addr || !size) return;
|
||||
uptr res = internal_munmap(addr, size);
|
||||
int reserrno;
|
||||
if (UNLIKELY(internal_iserror(res, &reserrno)))
|
||||
ReportMunmapFailureAndDie(addr, size, reserrno);
|
||||
ReportMunmapFailureAndDie(addr, size, reserrno, raw_report);
|
||||
DecreaseTotalMmap(size);
|
||||
}
|
||||
|
||||
@ -130,8 +130,8 @@ static void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem,
|
||||
if (tolerate_enomem && reserrno == ENOMEM)
|
||||
return nullptr;
|
||||
char mem_type[40];
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
|
||||
fixed_addr);
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p",
|
||||
(void *)fixed_addr);
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
|
||||
}
|
||||
IncreaseTotalMmap(size);
|
||||
|
||||
12
lib/tsan/sanitizer_common/sanitizer_posix.h
vendored
12
lib/tsan/sanitizer_common/sanitizer_posix.h
vendored
@ -74,18 +74,18 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
|
||||
// These functions call appropriate pthread_ functions directly, bypassing
|
||||
// the interceptor. They are weak and may not be present in some tools.
|
||||
SANITIZER_WEAK_ATTRIBUTE
|
||||
int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
|
||||
int internal_pthread_create(void *th, void *attr, void *(*callback)(void *),
|
||||
void *param);
|
||||
SANITIZER_WEAK_ATTRIBUTE
|
||||
int real_pthread_join(void *th, void **ret);
|
||||
int internal_pthread_join(void *th, void **ret);
|
||||
|
||||
#define DEFINE_REAL_PTHREAD_FUNCTIONS \
|
||||
# define DEFINE_INTERNAL_PTHREAD_FUNCTIONS \
|
||||
namespace __sanitizer { \
|
||||
int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \
|
||||
void *param) { \
|
||||
int internal_pthread_create(void *th, void *attr, \
|
||||
void *(*callback)(void *), void *param) { \
|
||||
return REAL(pthread_create)(th, attr, callback, param); \
|
||||
} \
|
||||
int real_pthread_join(void *th, void **ret) { \
|
||||
int internal_pthread_join(void *th, void **ret) { \
|
||||
return REAL(pthread_join(th, ret)); \
|
||||
} \
|
||||
} // namespace __sanitizer
|
||||
|
||||
@ -91,12 +91,12 @@ static rlim_t getlim(int res) {
|
||||
|
||||
static void setlim(int res, rlim_t lim) {
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(res, const_cast<struct rlimit *>(&rlim))) {
|
||||
if (getrlimit(res, &rlim)) {
|
||||
Report("ERROR: %s getrlimit() failed %d\n", SanitizerToolName, errno);
|
||||
Die();
|
||||
}
|
||||
rlim.rlim_cur = lim;
|
||||
if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
|
||||
if (setrlimit(res, &rlim)) {
|
||||
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
|
||||
Die();
|
||||
}
|
||||
@ -104,7 +104,27 @@ static void setlim(int res, rlim_t lim) {
|
||||
|
||||
void DisableCoreDumperIfNecessary() {
|
||||
if (common_flags()->disable_coredump) {
|
||||
setlim(RLIMIT_CORE, 0);
|
||||
rlimit rlim;
|
||||
CHECK_EQ(0, getrlimit(RLIMIT_CORE, &rlim));
|
||||
// On Linux, if the kernel.core_pattern sysctl starts with a '|' (i.e. it
|
||||
// is being piped to a coredump handler such as systemd-coredumpd), the
|
||||
// kernel ignores RLIMIT_CORE (since we aren't creating a file in the file
|
||||
// system) except for the magic value of 1, which disables coredumps when
|
||||
// piping. 1 byte is too small for any kind of valid core dump, so it
|
||||
// also disables coredumps if kernel.core_pattern creates files directly.
|
||||
// While most piped coredump handlers do respect the crashing processes'
|
||||
// RLIMIT_CORE, this is notable not the case for Debian's systemd-coredump
|
||||
// due to a local patch that changes sysctl.d/50-coredump.conf to ignore
|
||||
// the specified limit and instead use RLIM_INFINITY.
|
||||
//
|
||||
// The alternative to using RLIMIT_CORE=1 would be to use prctl() with the
|
||||
// PR_SET_DUMPABLE flag, however that also prevents ptrace(), so makes it
|
||||
// impossible to attach a debugger.
|
||||
//
|
||||
// Note: we use rlim_max in the Min() call here since that is the upper
|
||||
// limit for what can be set without getting an EINVAL error.
|
||||
rlim.rlim_cur = Min<rlim_t>(SANITIZER_LINUX ? 1 : 0, rlim.rlim_max);
|
||||
CHECK_EQ(0, setrlimit(RLIMIT_CORE, &rlim));
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,9 +327,10 @@ static bool MmapFixed(uptr fixed_addr, uptr size, int additional_flags,
|
||||
MAP_PRIVATE | MAP_FIXED | additional_flags | MAP_ANON, name);
|
||||
int reserrno;
|
||||
if (internal_iserror(p, &reserrno)) {
|
||||
Report("ERROR: %s failed to "
|
||||
"allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
|
||||
SanitizerToolName, size, size, fixed_addr, reserrno);
|
||||
Report(
|
||||
"ERROR: %s failed to "
|
||||
"allocate 0x%zx (%zd) bytes at address %p (errno: %d)\n",
|
||||
SanitizerToolName, size, size, (void *)fixed_addr, reserrno);
|
||||
return false;
|
||||
}
|
||||
IncreaseTotalMmap(size);
|
||||
|
||||
11
lib/tsan/sanitizer_common/sanitizer_printf.cpp
vendored
11
lib/tsan/sanitizer_common/sanitizer_printf.cpp
vendored
@ -54,7 +54,7 @@ static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
|
||||
uptr num_buffer[kMaxLen];
|
||||
int pos = 0;
|
||||
do {
|
||||
RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
|
||||
RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow",);
|
||||
num_buffer[pos++] = absolute_value % base;
|
||||
absolute_value /= base;
|
||||
} while (absolute_value > 0);
|
||||
@ -337,7 +337,14 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
|
||||
return needed_length;
|
||||
}
|
||||
|
||||
void InternalScopedString::append(const char *format, ...) {
|
||||
void InternalScopedString::Append(const char *str) {
|
||||
uptr prev_len = length();
|
||||
uptr str_len = internal_strlen(str);
|
||||
buffer_.resize(prev_len + str_len + 1);
|
||||
internal_memcpy(buffer_.data() + prev_len, str, str_len + 1);
|
||||
}
|
||||
|
||||
void InternalScopedString::AppendF(const char *format, ...) {
|
||||
uptr prev_len = length();
|
||||
|
||||
while (true) {
|
||||
|
||||
@ -13,9 +13,6 @@
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
#include "sanitizer_common.h"
|
||||
#if SANITIZER_FREEBSD
|
||||
#include "sanitizer_freebsd.h"
|
||||
#endif
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
// clang-format off
|
||||
@ -29,29 +26,35 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
|
||||
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||
#include <osreldate.h>
|
||||
#if __FreeBSD_version <= 902001 // v9.2
|
||||
#define kinfo_vmentry xkinfo_vmentry
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
|
||||
const int Mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
getpid()
|
||||
};
|
||||
const int Mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
|
||||
|
||||
struct kinfo_proc InfoProc;
|
||||
uptr Len = sizeof(InfoProc);
|
||||
CHECK_EQ(internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)&InfoProc, &Len, 0), 0);
|
||||
cb(0, InfoProc.ki_rssize * GetPageSizeCached(), false, stats);
|
||||
struct kinfo_proc *InfoProc;
|
||||
uptr Len = sizeof(*InfoProc);
|
||||
uptr Size = Len;
|
||||
InfoProc = (struct kinfo_proc *)MmapOrDie(Size, "GetMemoryProfile()");
|
||||
CHECK_EQ(
|
||||
internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0),
|
||||
0);
|
||||
cb(0, InfoProc->ki_rssize * GetPageSizeCached(), false, stats);
|
||||
UnmapOrDie(InfoProc, Size, true);
|
||||
}
|
||||
#elif SANITIZER_NETBSD
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
|
||||
struct kinfo_proc2 *InfoProc;
|
||||
uptr Len = sizeof(*InfoProc);
|
||||
uptr Size = Len;
|
||||
const int Mib[] = {CTL_KERN, KERN_PROC2, KERN_PROC_PID,
|
||||
getpid(), (int)Size, 1};
|
||||
InfoProc = (struct kinfo_proc2 *)MmapOrDie(Size, "GetMemoryProfile()");
|
||||
CHECK_EQ(
|
||||
internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0),
|
||||
0);
|
||||
cb(0, InfoProc->p_vm_rssize * GetPageSizeCached(), false, stats);
|
||||
UnmapOrDie(InfoProc, Size, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ void MemoryMappingLayout::DumpListOfModules(
|
||||
}
|
||||
}
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS || SANITIZER_NETBSD
|
||||
#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
|
||||
char *smaps = nullptr;
|
||||
uptr smaps_cap = 0;
|
||||
|
||||
26
lib/tsan/sanitizer_common/sanitizer_ptrauth.h
vendored
26
lib/tsan/sanitizer_common/sanitizer_ptrauth.h
vendored
@ -9,24 +9,26 @@
|
||||
#ifndef SANITIZER_PTRAUTH_H
|
||||
#define SANITIZER_PTRAUTH_H
|
||||
|
||||
#if __has_feature(ptrauth_calls)
|
||||
#if __has_feature(ptrauth_intrinsics)
|
||||
# include <ptrauth.h>
|
||||
#elif defined(__ARM_FEATURE_PAC_DEFAULT) && !defined(__APPLE__)
|
||||
inline unsigned long ptrauth_strip(void* __value, unsigned int __key) {
|
||||
// On the stack the link register is protected with Pointer
|
||||
// Authentication Code when compiled with -mbranch-protection.
|
||||
// Let's stripping the PAC unconditionally because xpaclri is in
|
||||
// the NOP space so will do nothing when it is not enabled or not available.
|
||||
unsigned long ret;
|
||||
asm volatile(
|
||||
"mov x30, %1\n\t"
|
||||
"hint #7\n\t" // xpaclri
|
||||
"mov %0, x30\n\t"
|
||||
: "=r"(ret)
|
||||
: "r"(__value)
|
||||
: "x30");
|
||||
return ret;
|
||||
}
|
||||
# define ptrauth_strip(__value, __key) \
|
||||
({ \
|
||||
__typeof(__value) ret; \
|
||||
asm volatile( \
|
||||
"mov x30, %1\n\t" \
|
||||
"hint #7\n\t" \
|
||||
"mov %0, x30\n\t" \
|
||||
"mov x30, xzr\n\t" \
|
||||
: "=r"(ret) \
|
||||
: "r"(__value) \
|
||||
: "x30"); \
|
||||
ret; \
|
||||
})
|
||||
# define ptrauth_auth_data(__value, __old_key, __old_data) __value
|
||||
# define ptrauth_string_discriminator(__string) ((int)0)
|
||||
#else
|
||||
|
||||
@ -21,6 +21,9 @@ asm("memcpy = __sanitizer_internal_memcpy");
|
||||
asm("memmove = __sanitizer_internal_memmove");
|
||||
asm("memset = __sanitizer_internal_memset");
|
||||
|
||||
# if defined(__cplusplus) && \
|
||||
!defined(SANITIZER_COMMON_REDEFINE_BUILTINS_IN_STD)
|
||||
|
||||
// The builtins should not be redefined in source files that make use of C++
|
||||
// standard libraries, in particular where C++STL headers with inline functions
|
||||
// are used. The redefinition in such cases would lead to ODR violations.
|
||||
@ -46,6 +49,7 @@ using unordered_set = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
|
||||
using vector = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
|
||||
} // namespace std
|
||||
|
||||
# endif // __cpluplus
|
||||
# endif // !_WIN32
|
||||
|
||||
# endif // SANITIZER_REDEFINE_BUILTINS_H
|
||||
|
||||
@ -47,7 +47,9 @@ class RingBuffer {
|
||||
void push(T t) {
|
||||
*next_ = t;
|
||||
next_--;
|
||||
// The condition below works only if sizeof(T) is divisible by sizeof(T*).
|
||||
static_assert((sizeof(T) % sizeof(T *)) == 0,
|
||||
"The condition below works only if sizeof(T) is divisible by "
|
||||
"sizeof(T*).");
|
||||
if (next_ <= reinterpret_cast<T*>(&next_))
|
||||
next_ = last_;
|
||||
}
|
||||
|
||||
@ -44,6 +44,9 @@ StackStore::Id StackStore::Store(const StackTrace &trace, uptr *pack) {
|
||||
uptr idx = 0;
|
||||
*pack = 0;
|
||||
uptr *stack_trace = Alloc(h.size + 1, &idx, pack);
|
||||
// No more space.
|
||||
if (stack_trace == nullptr)
|
||||
return 0;
|
||||
*stack_trace = h.ToUptr();
|
||||
internal_memcpy(stack_trace + 1, trace.trace, h.size * sizeof(uptr));
|
||||
*pack += blocks_[GetBlockIdx(idx)].Stored(h.size + 1);
|
||||
@ -76,8 +79,10 @@ uptr *StackStore::Alloc(uptr count, uptr *idx, uptr *pack) {
|
||||
uptr block_idx = GetBlockIdx(start);
|
||||
uptr last_idx = GetBlockIdx(start + count - 1);
|
||||
if (LIKELY(block_idx == last_idx)) {
|
||||
// Fits into the a single block.
|
||||
CHECK_LT(block_idx, ARRAY_SIZE(blocks_));
|
||||
// Fits into a single block.
|
||||
// No more available blocks. Indicate inability to allocate more memory.
|
||||
if (block_idx >= ARRAY_SIZE(blocks_))
|
||||
return nullptr;
|
||||
*idx = start;
|
||||
return blocks_[block_idx].GetOrCreate(this) + GetInBlockIdx(start);
|
||||
}
|
||||
|
||||
@ -215,16 +215,16 @@ StackTrace StackDepotGet(u32 id) {
|
||||
return theDepot.Get(id);
|
||||
}
|
||||
|
||||
void StackDepotLockAll() {
|
||||
theDepot.LockAll();
|
||||
void StackDepotLockBeforeFork() {
|
||||
theDepot.LockBeforeFork();
|
||||
compress_thread.LockAndStop();
|
||||
stackStore.LockAll();
|
||||
}
|
||||
|
||||
void StackDepotUnlockAll() {
|
||||
void StackDepotUnlockAfterFork(bool fork_child) {
|
||||
stackStore.UnlockAll();
|
||||
compress_thread.Unlock();
|
||||
theDepot.UnlockAll();
|
||||
theDepot.UnlockAfterFork(fork_child);
|
||||
}
|
||||
|
||||
void StackDepotPrintAll() {
|
||||
|
||||
@ -39,8 +39,8 @@ StackDepotHandle StackDepotPut_WithHandle(StackTrace stack);
|
||||
// Retrieves a stored stack trace by the id.
|
||||
StackTrace StackDepotGet(u32 id);
|
||||
|
||||
void StackDepotLockAll();
|
||||
void StackDepotUnlockAll();
|
||||
void StackDepotLockBeforeFork();
|
||||
void StackDepotUnlockAfterFork(bool fork_child);
|
||||
void StackDepotPrintAll();
|
||||
void StackDepotStopBackgroundThread();
|
||||
|
||||
|
||||
@ -52,8 +52,8 @@ class StackDepotBase {
|
||||
};
|
||||
}
|
||||
|
||||
void LockAll();
|
||||
void UnlockAll();
|
||||
void LockBeforeFork();
|
||||
void UnlockAfterFork(bool fork_child);
|
||||
void PrintAll();
|
||||
|
||||
void TestOnlyUnmap() {
|
||||
@ -160,17 +160,32 @@ StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
|
||||
}
|
||||
|
||||
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
|
||||
for (int i = 0; i < kTabSize; ++i) {
|
||||
lock(&tab[i]);
|
||||
}
|
||||
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockBeforeFork() {
|
||||
// Do not lock hash table. It's very expensive, but it's not rely needed. The
|
||||
// parent process will neither lock nor unlock. Child process risks to be
|
||||
// deadlocked on already locked buckets. To avoid deadlock we will unlock
|
||||
// every locked buckets in `UnlockAfterFork`. This may affect consistency of
|
||||
// the hash table, but the only issue is a few items inserted by parent
|
||||
// process will be not found by child, and the child may insert them again,
|
||||
// wasting some space in `stackStore`.
|
||||
|
||||
// We still need to lock nodes.
|
||||
nodes.Lock();
|
||||
}
|
||||
|
||||
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
|
||||
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAfterFork(
|
||||
bool fork_child) {
|
||||
nodes.Unlock();
|
||||
|
||||
// Only unlock in child process to avoid deadlock. See `LockBeforeFork`.
|
||||
if (!fork_child)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < kTabSize; ++i) {
|
||||
atomic_uint32_t *p = &tab[i];
|
||||
uptr s = atomic_load(p, memory_order_relaxed);
|
||||
if (s & kLockMask)
|
||||
unlock(p, s & kUnlockMask);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,42 +29,43 @@ class StackTraceTextPrinter {
|
||||
frame_delimiter_(frame_delimiter),
|
||||
output_(output),
|
||||
dedup_token_(dedup_token),
|
||||
symbolize_(RenderNeedsSymbolization(stack_trace_fmt)) {}
|
||||
symbolize_(StackTracePrinter::GetOrInit()->RenderNeedsSymbolization(
|
||||
stack_trace_fmt)) {}
|
||||
|
||||
bool ProcessAddressFrames(uptr pc) {
|
||||
SymbolizedStack *frames = symbolize_
|
||||
? Symbolizer::GetOrInit()->SymbolizePC(pc)
|
||||
: SymbolizedStack::New(pc);
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc)
|
||||
: SymbolizedStack::New(pc));
|
||||
const SymbolizedStack *frames = symbolized_stack.get();
|
||||
if (!frames)
|
||||
return false;
|
||||
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
uptr prev_len = output_->length();
|
||||
RenderFrame(output_, stack_trace_fmt_, frame_num_++, cur->info.address,
|
||||
symbolize_ ? &cur->info : nullptr,
|
||||
common_flags()->symbolize_vs_style,
|
||||
StackTracePrinter::GetOrInit()->RenderFrame(
|
||||
output_, stack_trace_fmt_, frame_num_++, cur->info.address,
|
||||
symbolize_ ? &cur->info : nullptr, common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
|
||||
if (prev_len != output_->length())
|
||||
output_->append("%c", frame_delimiter_);
|
||||
output_->AppendF("%c", frame_delimiter_);
|
||||
|
||||
ExtendDedupToken(cur);
|
||||
}
|
||||
frames->ClearAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Extend the dedup token by appending a new frame.
|
||||
void ExtendDedupToken(SymbolizedStack *stack) {
|
||||
void ExtendDedupToken(const SymbolizedStack *stack) {
|
||||
if (!dedup_token_)
|
||||
return;
|
||||
|
||||
if (dedup_frames_-- > 0) {
|
||||
if (dedup_token_->length())
|
||||
dedup_token_->append("--");
|
||||
if (stack->info.function != nullptr)
|
||||
dedup_token_->append("%s", stack->info.function);
|
||||
dedup_token_->Append("--");
|
||||
if (stack->info.function)
|
||||
dedup_token_->Append(stack->info.function);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +99,7 @@ void StackTrace::PrintTo(InternalScopedString *output) const {
|
||||
output, &dedup_token);
|
||||
|
||||
if (trace == nullptr || size == 0) {
|
||||
output->append(" <empty stack>\n\n");
|
||||
output->Append(" <empty stack>\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,11 +111,11 @@ void StackTrace::PrintTo(InternalScopedString *output) const {
|
||||
}
|
||||
|
||||
// Always add a trailing empty line after stack trace.
|
||||
output->append("\n");
|
||||
output->Append("\n");
|
||||
|
||||
// Append deduplication token, if non-empty.
|
||||
if (dedup_token.length())
|
||||
output->append("DEDUP_TOKEN: %s\n", dedup_token.data());
|
||||
output->AppendF("DEDUP_TOKEN: %s\n", dedup_token.data());
|
||||
}
|
||||
|
||||
uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const {
|
||||
@ -197,7 +198,7 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
|
||||
StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
|
||||
if (!printer.ProcessAddressFrames(pc)) {
|
||||
output.clear();
|
||||
output.append("<can't symbolize>");
|
||||
output.Append("<can't symbolize>");
|
||||
}
|
||||
CopyStringToBuffer(output, out_buf, out_buf_size);
|
||||
}
|
||||
@ -210,7 +211,8 @@ void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
|
||||
DataInfo DI;
|
||||
if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
|
||||
InternalScopedString data_desc;
|
||||
RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix);
|
||||
StackTracePrinter::GetOrInit()->RenderData(&data_desc, fmt, &DI,
|
||||
common_flags()->strip_path_prefix);
|
||||
internal_strncpy(out_buf, data_desc.data(), out_buf_size);
|
||||
out_buf[out_buf_size - 1] = 0;
|
||||
}
|
||||
|
||||
@ -12,13 +12,28 @@
|
||||
|
||||
#include "sanitizer_stacktrace_printer.h"
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_file.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_fuchsia.h"
|
||||
#include "sanitizer_symbolizer_markup.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
const char *StripFunctionName(const char *function) {
|
||||
StackTracePrinter *StackTracePrinter::GetOrInit() {
|
||||
static StackTracePrinter *stacktrace_printer;
|
||||
static StaticSpinMutex init_mu;
|
||||
SpinMutexLock l(&init_mu);
|
||||
if (stacktrace_printer)
|
||||
return stacktrace_printer;
|
||||
|
||||
stacktrace_printer = StackTracePrinter::NewStackTracePrinter();
|
||||
|
||||
CHECK(stacktrace_printer);
|
||||
return stacktrace_printer;
|
||||
}
|
||||
|
||||
const char *StackTracePrinter::StripFunctionName(const char *function) {
|
||||
if (!common_flags()->demangle)
|
||||
return function;
|
||||
if (!function)
|
||||
@ -47,6 +62,13 @@ const char *StripFunctionName(const char *function) {
|
||||
// sanitizer_symbolizer_markup.cpp implements these differently.
|
||||
#if !SANITIZER_SYMBOLIZER_MARKUP
|
||||
|
||||
StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
|
||||
if (common_flags()->enable_symbolizer_markup)
|
||||
return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
|
||||
|
||||
return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
|
||||
}
|
||||
|
||||
static const char *DemangleFunctionName(const char *function) {
|
||||
if (!common_flags()->demangle)
|
||||
return function;
|
||||
@ -130,19 +152,22 @@ static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
|
||||
InternalScopedString *buffer) {
|
||||
if (info.uuid_size) {
|
||||
if (PrefixSpace)
|
||||
buffer->append(" ");
|
||||
buffer->append("(BuildId: ");
|
||||
buffer->Append(" ");
|
||||
buffer->Append("(BuildId: ");
|
||||
for (uptr i = 0; i < info.uuid_size; ++i) {
|
||||
buffer->append("%02x", info.uuid[i]);
|
||||
buffer->AppendF("%02x", info.uuid[i]);
|
||||
}
|
||||
buffer->append(")");
|
||||
buffer->Append(")");
|
||||
}
|
||||
}
|
||||
|
||||
static const char kDefaultFormat[] = " #%n %p %F %L";
|
||||
|
||||
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
||||
uptr address, const AddressInfo *info, bool vs_style,
|
||||
void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
|
||||
const char *format, int frame_no,
|
||||
uptr address,
|
||||
const AddressInfo *info,
|
||||
bool vs_style,
|
||||
const char *strip_path_prefix) {
|
||||
// info will be null in the case where symbolization is not needed for the
|
||||
// given format. This ensures that the code below will get a hard failure
|
||||
@ -154,56 +179,56 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
||||
format = kDefaultFormat;
|
||||
for (const char *p = format; *p != '\0'; p++) {
|
||||
if (*p != '%') {
|
||||
buffer->append("%c", *p);
|
||||
buffer->AppendF("%c", *p);
|
||||
continue;
|
||||
}
|
||||
p++;
|
||||
switch (*p) {
|
||||
case '%':
|
||||
buffer->append("%%");
|
||||
buffer->Append("%");
|
||||
break;
|
||||
// Frame number and all fields of AddressInfo structure.
|
||||
case 'n':
|
||||
buffer->append("%u", frame_no);
|
||||
buffer->AppendF("%u", frame_no);
|
||||
break;
|
||||
case 'p':
|
||||
buffer->append("0x%zx", address);
|
||||
buffer->AppendF("%p", (void *)address);
|
||||
break;
|
||||
case 'm':
|
||||
buffer->append("%s", StripPathPrefix(info->module, strip_path_prefix));
|
||||
buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix));
|
||||
break;
|
||||
case 'o':
|
||||
buffer->append("0x%zx", info->module_offset);
|
||||
buffer->AppendF("0x%zx", info->module_offset);
|
||||
break;
|
||||
case 'b':
|
||||
MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer);
|
||||
break;
|
||||
case 'f':
|
||||
buffer->append("%s",
|
||||
buffer->AppendF("%s",
|
||||
DemangleFunctionName(StripFunctionName(info->function)));
|
||||
break;
|
||||
case 'q':
|
||||
buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
|
||||
buffer->AppendF("0x%zx", info->function_offset != AddressInfo::kUnknown
|
||||
? info->function_offset
|
||||
: 0x0);
|
||||
break;
|
||||
case 's':
|
||||
buffer->append("%s", StripPathPrefix(info->file, strip_path_prefix));
|
||||
buffer->AppendF("%s", StripPathPrefix(info->file, strip_path_prefix));
|
||||
break;
|
||||
case 'l':
|
||||
buffer->append("%d", info->line);
|
||||
buffer->AppendF("%d", info->line);
|
||||
break;
|
||||
case 'c':
|
||||
buffer->append("%d", info->column);
|
||||
buffer->AppendF("%d", info->column);
|
||||
break;
|
||||
// Smarter special cases.
|
||||
case 'F':
|
||||
// Function name and offset, if file is unknown.
|
||||
if (info->function) {
|
||||
buffer->append("in %s",
|
||||
DemangleFunctionName(StripFunctionName(info->function)));
|
||||
buffer->AppendF(
|
||||
"in %s", DemangleFunctionName(StripFunctionName(info->function)));
|
||||
if (!info->file && info->function_offset != AddressInfo::kUnknown)
|
||||
buffer->append("+0x%zx", info->function_offset);
|
||||
buffer->AppendF("+0x%zx", info->function_offset);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
@ -224,7 +249,7 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
||||
MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
|
||||
#endif
|
||||
} else {
|
||||
buffer->append("(<unknown module>)");
|
||||
buffer->Append("(<unknown module>)");
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
@ -239,18 +264,18 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
||||
MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
|
||||
#endif
|
||||
} else {
|
||||
buffer->append("(%p)", (void *)address);
|
||||
buffer->AppendF("(%p)", (void *)address);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
|
||||
(void *)p);
|
||||
(const void *)p);
|
||||
Die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderNeedsSymbolization(const char *format) {
|
||||
bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) {
|
||||
if (0 == internal_strcmp(format, "DEFAULT"))
|
||||
format = kDefaultFormat;
|
||||
for (const char *p = format; *p != '\0'; p++) {
|
||||
@ -273,30 +298,32 @@ bool RenderNeedsSymbolization(const char *format) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderData(InternalScopedString *buffer, const char *format,
|
||||
const DataInfo *DI, const char *strip_path_prefix) {
|
||||
void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer,
|
||||
const char *format,
|
||||
const DataInfo *DI,
|
||||
const char *strip_path_prefix) {
|
||||
for (const char *p = format; *p != '\0'; p++) {
|
||||
if (*p != '%') {
|
||||
buffer->append("%c", *p);
|
||||
buffer->AppendF("%c", *p);
|
||||
continue;
|
||||
}
|
||||
p++;
|
||||
switch (*p) {
|
||||
case '%':
|
||||
buffer->append("%%");
|
||||
buffer->Append("%");
|
||||
break;
|
||||
case 's':
|
||||
buffer->append("%s", StripPathPrefix(DI->file, strip_path_prefix));
|
||||
buffer->AppendF("%s", StripPathPrefix(DI->file, strip_path_prefix));
|
||||
break;
|
||||
case 'l':
|
||||
buffer->append("%zu", DI->line);
|
||||
buffer->AppendF("%zu", DI->line);
|
||||
break;
|
||||
case 'g':
|
||||
buffer->append("%s", DI->name);
|
||||
buffer->AppendF("%s", DI->name);
|
||||
break;
|
||||
default:
|
||||
Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
|
||||
(void *)p);
|
||||
(const void *)p);
|
||||
Die();
|
||||
}
|
||||
}
|
||||
@ -304,33 +331,35 @@ void RenderData(InternalScopedString *buffer, const char *format,
|
||||
|
||||
#endif // !SANITIZER_SYMBOLIZER_MARKUP
|
||||
|
||||
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
|
||||
int line, int column, bool vs_style,
|
||||
void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer,
|
||||
const char *file, int line,
|
||||
int column, bool vs_style,
|
||||
const char *strip_path_prefix) {
|
||||
if (vs_style && line > 0) {
|
||||
buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
|
||||
buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
|
||||
if (column > 0)
|
||||
buffer->append(",%d", column);
|
||||
buffer->append(")");
|
||||
buffer->AppendF(",%d", column);
|
||||
buffer->Append(")");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
|
||||
buffer->AppendF("%s", StripPathPrefix(file, strip_path_prefix));
|
||||
if (line > 0) {
|
||||
buffer->append(":%d", line);
|
||||
buffer->AppendF(":%d", line);
|
||||
if (column > 0)
|
||||
buffer->append(":%d", column);
|
||||
buffer->AppendF(":%d", column);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
|
||||
uptr offset, ModuleArch arch,
|
||||
void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer,
|
||||
const char *module, uptr offset,
|
||||
ModuleArch arch,
|
||||
const char *strip_path_prefix) {
|
||||
buffer->append("(%s", StripPathPrefix(module, strip_path_prefix));
|
||||
buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix));
|
||||
if (arch != kModuleArchUnknown) {
|
||||
buffer->append(":%s", ModuleArchToString(arch));
|
||||
buffer->AppendF(":%s", ModuleArchToString(arch));
|
||||
}
|
||||
buffer->append("+0x%zx)", offset);
|
||||
buffer->AppendF("+0x%zx)", offset);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
@ -13,13 +13,57 @@
|
||||
#define SANITIZER_STACKTRACE_PRINTER_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// StacktracePrinter is an interface that is implemented by
|
||||
// classes that can perform rendering of the different parts
|
||||
// of a stacktrace.
|
||||
class StackTracePrinter {
|
||||
public:
|
||||
static StackTracePrinter *GetOrInit();
|
||||
|
||||
// Strip interceptor prefixes from function name.
|
||||
const char *StripFunctionName(const char *function);
|
||||
|
||||
virtual void RenderFrame(InternalScopedString *buffer, const char *format,
|
||||
int frame_no, uptr address, const AddressInfo *info,
|
||||
bool vs_style, const char *strip_path_prefix = "") {
|
||||
// Should be pure virtual, but we can't depend on __cxa_pure_virtual.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
virtual bool RenderNeedsSymbolization(const char *format) {
|
||||
// Should be pure virtual, but we can't depend on __cxa_pure_virtual.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
|
||||
int line, int column, bool vs_style,
|
||||
const char *strip_path_prefix);
|
||||
|
||||
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
|
||||
uptr offset, ModuleArch arch,
|
||||
const char *strip_path_prefix);
|
||||
virtual void RenderData(InternalScopedString *buffer, const char *format,
|
||||
const DataInfo *DI,
|
||||
const char *strip_path_prefix = "") {
|
||||
// Should be pure virtual, but we can't depend on __cxa_pure_virtual.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
private:
|
||||
// To be called from StackTracePrinter::GetOrInit
|
||||
static StackTracePrinter *NewStackTracePrinter();
|
||||
|
||||
protected:
|
||||
~StackTracePrinter() {}
|
||||
};
|
||||
|
||||
class FormattedStackTracePrinter : public StackTracePrinter {
|
||||
public:
|
||||
// Render the contents of "info" structure, which represents the contents of
|
||||
// stack frame "frame_no" and appends it to the "buffer". "format" is a
|
||||
// string with placeholders, which is copied to the output with
|
||||
@ -48,26 +92,23 @@ const char *StripFunctionName(const char *function);
|
||||
// %L - prints location information: file/line/column, if it is known, or
|
||||
// module+offset if it is known, or (<unknown module>) string.
|
||||
// %M - prints module basename and offset, if it is known, or PC.
|
||||
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
||||
uptr address, const AddressInfo *info, bool vs_style,
|
||||
const char *strip_path_prefix = "");
|
||||
void RenderFrame(InternalScopedString *buffer, const char *format,
|
||||
int frame_no, uptr address, const AddressInfo *info,
|
||||
bool vs_style, const char *strip_path_prefix = "") override;
|
||||
|
||||
bool RenderNeedsSymbolization(const char *format);
|
||||
|
||||
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
|
||||
int line, int column, bool vs_style,
|
||||
const char *strip_path_prefix);
|
||||
|
||||
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
|
||||
uptr offset, ModuleArch arch,
|
||||
const char *strip_path_prefix);
|
||||
bool RenderNeedsSymbolization(const char *format) override;
|
||||
|
||||
// Same as RenderFrame, but for data section (global variables).
|
||||
// Accepts %s, %l from above.
|
||||
// Also accepts:
|
||||
// %g - name of the global variable.
|
||||
void RenderData(InternalScopedString *buffer, const char *format,
|
||||
const DataInfo *DI, const char *strip_path_prefix = "");
|
||||
const DataInfo *DI,
|
||||
const char *strip_path_prefix = "") override;
|
||||
|
||||
protected:
|
||||
~FormattedStackTracePrinter() {}
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
|
||||
@ -58,17 +58,16 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
|
||||
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
|
||||
while (IsValidFrame(bp, stack_top, bottom) && IsAligned(bp, sizeof(uhwptr)) &&
|
||||
size < max_depth) {
|
||||
uhwptr pc1 = ((uhwptr *)bp)[15];
|
||||
// %o7 contains the address of the call instruction and not the
|
||||
// return address, so we need to compensate.
|
||||
uhwptr pc1 = GetNextInstructionPc(((uhwptr *)bp)[15]);
|
||||
// Let's assume that any pointer in the 0th page is invalid and
|
||||
// stop unwinding here. If we're adding support for a platform
|
||||
// where this isn't true, we need to reconsider this check.
|
||||
if (pc1 < kPageSize)
|
||||
break;
|
||||
if (pc1 != pc) {
|
||||
// %o7 contains the address of the call instruction and not the
|
||||
// return address, so we need to compensate.
|
||||
trace_buffer[size++] = GetNextInstructionPc((uptr)pc1);
|
||||
}
|
||||
if (pc1 != pc)
|
||||
trace_buffer[size++] = pc1;
|
||||
bottom = bp;
|
||||
bp = (uptr)((uhwptr *)bp)[14] + STACK_BIAS;
|
||||
}
|
||||
|
||||
@ -257,8 +257,8 @@ static void TracerThreadDieCallback() {
|
||||
static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
|
||||
void *uctx) {
|
||||
SignalContext ctx(siginfo, uctx);
|
||||
Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
|
||||
ctx.addr, ctx.pc, ctx.sp);
|
||||
Printf("Tracer caught signal %d: addr=%p pc=%p sp=%p\n", signum,
|
||||
(void *)ctx.addr, (void *)ctx.pc, (void *)ctx.sp);
|
||||
ThreadSuspender *inst = thread_suspender_instance;
|
||||
if (inst) {
|
||||
if (signum == SIGABRT)
|
||||
@ -565,7 +565,7 @@ PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP(
|
||||
constexpr uptr uptr_sz = sizeof(uptr);
|
||||
int pterrno;
|
||||
#ifdef ARCH_IOVEC_FOR_GETREGSET
|
||||
auto append = [&](uptr regset) {
|
||||
auto AppendF = [&](uptr regset) {
|
||||
uptr size = buffer->size();
|
||||
// NT_X86_XSTATE requires 64bit alignment.
|
||||
uptr size_up = RoundUpTo(size, 8 / uptr_sz);
|
||||
@ -596,11 +596,11 @@ PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP(
|
||||
};
|
||||
|
||||
buffer->clear();
|
||||
bool fail = !append(NT_PRSTATUS);
|
||||
bool fail = !AppendF(NT_PRSTATUS);
|
||||
if (!fail) {
|
||||
// Accept the first available and do not report errors.
|
||||
for (uptr regs : kExtraRegs)
|
||||
if (regs && append(regs))
|
||||
if (regs && AppendF(regs))
|
||||
break;
|
||||
}
|
||||
#else
|
||||
|
||||
@ -158,8 +158,8 @@ static void TracerThreadDieCallback() {
|
||||
static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
|
||||
void *uctx) {
|
||||
SignalContext ctx(siginfo, uctx);
|
||||
Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
|
||||
ctx.addr, ctx.pc, ctx.sp);
|
||||
Printf("Tracer caught signal %d: addr=%p pc=%p sp=%p\n", signum,
|
||||
(void *)ctx.addr, (void *)ctx.pc, (void *)ctx.sp);
|
||||
ThreadSuspender *inst = thread_suspender_instance;
|
||||
if (inst) {
|
||||
if (signum == SIGABRT)
|
||||
|
||||
@ -86,7 +86,7 @@ void SuppressionContext::ParseFromFile(const char *filename) {
|
||||
}
|
||||
|
||||
Parse(file_contents);
|
||||
UnmapOrDie(file_contents, contents_size);
|
||||
UnmapOrDie(file_contents, buffer_size);
|
||||
}
|
||||
|
||||
bool SuppressionContext::Match(const char *str, const char *type,
|
||||
@ -138,7 +138,10 @@ void SuppressionContext::Parse(const char *str) {
|
||||
}
|
||||
}
|
||||
if (type == suppression_types_num_) {
|
||||
Printf("%s: failed to parse suppressions\n", SanitizerToolName);
|
||||
Printf("%s: failed to parse suppressions.\n", SanitizerToolName);
|
||||
Printf("Supported suppression types are:\n");
|
||||
for (type = 0; type < suppression_types_num_; type++)
|
||||
Printf("- %s\n", suppression_types_[type]);
|
||||
Die();
|
||||
}
|
||||
Suppression s;
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
// run-time libraries.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
@ -128,7 +130,7 @@ Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
|
||||
start_hook_(0), end_hook_(0) {}
|
||||
|
||||
Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
|
||||
: sym_(sym) {
|
||||
: sym_(sym), errno_(errno) {
|
||||
if (sym_->start_hook_)
|
||||
sym_->start_hook_();
|
||||
}
|
||||
@ -136,6 +138,7 @@ Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
|
||||
Symbolizer::SymbolizerScope::~SymbolizerScope() {
|
||||
if (sym_->end_hook_)
|
||||
sym_->end_hook_();
|
||||
errno = errno_;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
27
lib/tsan/sanitizer_common/sanitizer_symbolizer.h
vendored
27
lib/tsan/sanitizer_common/sanitizer_symbolizer.h
vendored
@ -64,6 +64,26 @@ struct SymbolizedStack {
|
||||
SymbolizedStack();
|
||||
};
|
||||
|
||||
class SymbolizedStackHolder {
|
||||
SymbolizedStack *Stack;
|
||||
|
||||
void clear() {
|
||||
if (Stack)
|
||||
Stack->ClearAll();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr)
|
||||
: Stack(Stack) {}
|
||||
~SymbolizedStackHolder() { clear(); }
|
||||
void reset(SymbolizedStack *S = nullptr) {
|
||||
if (Stack != S)
|
||||
clear();
|
||||
Stack = S;
|
||||
}
|
||||
const SymbolizedStack *get() const { return Stack; }
|
||||
};
|
||||
|
||||
// For now, DataInfo is used to describe global variable.
|
||||
struct DataInfo {
|
||||
// Owns all the string members. Storage for them is
|
||||
@ -136,7 +156,7 @@ class Symbolizer final {
|
||||
|
||||
// Release internal caches (if any).
|
||||
void Flush();
|
||||
// Attempts to demangle the provided C++ mangled name.
|
||||
// Attempts to demangle the provided C++ mangled name. Never returns nullptr.
|
||||
const char *Demangle(const char *name);
|
||||
|
||||
// Allow user to install hooks that would be called before/after Symbolizer
|
||||
@ -154,6 +174,8 @@ class Symbolizer final {
|
||||
|
||||
void InvalidateModuleList();
|
||||
|
||||
const ListOfModules &GetRefreshedListOfModules();
|
||||
|
||||
private:
|
||||
// GetModuleNameAndOffsetForPC has to return a string to the caller.
|
||||
// Since the corresponding module might get unloaded later, we should create
|
||||
@ -187,7 +209,7 @@ class Symbolizer final {
|
||||
// If stale, need to reload the modules before looking up addresses.
|
||||
bool modules_fresh_;
|
||||
|
||||
// Platform-specific default demangler, must not return nullptr.
|
||||
// Platform-specific default demangler, returns nullptr on failure.
|
||||
const char *PlatformDemangle(const char *name);
|
||||
|
||||
static Symbolizer *symbolizer_;
|
||||
@ -212,6 +234,7 @@ class Symbolizer final {
|
||||
~SymbolizerScope();
|
||||
private:
|
||||
const Symbolizer *sym_;
|
||||
int errno_; // Backup errno in case symbolizer change the value.
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -160,6 +160,15 @@ void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res);
|
||||
// Used by LLVMSymbolizer and InternalSymbolizer.
|
||||
void ParseSymbolizeDataOutput(const char *str, DataInfo *info);
|
||||
|
||||
// Parses repeated strings in the following format:
|
||||
// <function_name>
|
||||
// <var_name>
|
||||
// <file_name>:<line_number>[:<column_number>]
|
||||
// [<frame_offset>|??] [<size>|??] [<tag_offset>|??]
|
||||
// Used by LLVMSymbolizer and InternalSymbolizer.
|
||||
void ParseSymbolizeFrameOutput(const char *str,
|
||||
InternalMmapVector<LocalInfo> *locals);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_INTERNAL_H
|
||||
|
||||
@ -199,7 +199,7 @@ static char *DemangleAlloc(const char *name, bool always_alloc) {
|
||||
#endif
|
||||
if (always_alloc)
|
||||
return internal_strdup(name);
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *LibbacktraceSymbolizer::Demangle(const char *name) {
|
||||
|
||||
@ -117,7 +117,7 @@ bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {
|
||||
@ -133,7 +133,7 @@ bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
|
||||
@ -159,13 +159,16 @@ void Symbolizer::Flush() {
|
||||
}
|
||||
|
||||
const char *Symbolizer::Demangle(const char *name) {
|
||||
CHECK(name);
|
||||
Lock l(&mu_);
|
||||
for (auto &tool : tools_) {
|
||||
SymbolizerScope sym_scope(this);
|
||||
if (const char *demangled = tool.Demangle(name))
|
||||
return demangled;
|
||||
}
|
||||
return PlatformDemangle(name);
|
||||
if (const char *demangled = PlatformDemangle(name))
|
||||
return demangled;
|
||||
return name;
|
||||
}
|
||||
|
||||
bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
|
||||
@ -188,6 +191,13 @@ void Symbolizer::RefreshModules() {
|
||||
modules_fresh_ = true;
|
||||
}
|
||||
|
||||
const ListOfModules &Symbolizer::GetRefreshedListOfModules() {
|
||||
if (!modules_fresh_)
|
||||
RefreshModules();
|
||||
|
||||
return modules_;
|
||||
}
|
||||
|
||||
static const LoadedModule *SearchForModule(const ListOfModules &modules,
|
||||
uptr address) {
|
||||
for (uptr i = 0; i < modules.size(); i++) {
|
||||
@ -382,7 +392,7 @@ void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
|
||||
str = ExtractUptr(str, "\n", &info->line);
|
||||
}
|
||||
|
||||
static void ParseSymbolizeFrameOutput(const char *str,
|
||||
void ParseSymbolizeFrameOutput(const char *str,
|
||||
InternalMmapVector<LocalInfo> *locals) {
|
||||
if (internal_strncmp(str, "??", 2) == 0)
|
||||
return;
|
||||
|
||||
@ -42,7 +42,8 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
|
||||
}
|
||||
|
||||
const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
|
||||
if (!demangled) return false;
|
||||
if (!demangled)
|
||||
demangled = info.dli_sname;
|
||||
stack->info.function = internal_strdup(demangled);
|
||||
return true;
|
||||
}
|
||||
@ -52,6 +53,8 @@ bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
|
||||
int result = dladdr((const void *)addr, &info);
|
||||
if (!result) return false;
|
||||
const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
|
||||
if (!demangled)
|
||||
demangled = info.dli_sname;
|
||||
datainfo->name = internal_strdup(demangled);
|
||||
datainfo->start = (uptr)info.dli_saddr;
|
||||
return true;
|
||||
|
||||
@ -8,143 +8,155 @@
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries.
|
||||
//
|
||||
// Implementation of offline markup symbolizer.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_SYMBOLIZER_MARKUP
|
||||
|
||||
#if SANITIZER_FUCHSIA
|
||||
#include "sanitizer_symbolizer_fuchsia.h"
|
||||
# endif
|
||||
|
||||
# include <limits.h>
|
||||
# include <unwind.h>
|
||||
|
||||
# include "sanitizer_stacktrace.h"
|
||||
# include "sanitizer_symbolizer.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// This generic support for offline symbolizing is based on the
|
||||
// Fuchsia port. We don't do any actual symbolization per se.
|
||||
// Instead, we emit text containing raw addresses and raw linkage
|
||||
// symbol names, embedded in Fuchsia's symbolization markup format.
|
||||
// Fuchsia's logging infrastructure emits enough information about
|
||||
// process memory layout that a post-processing filter can do the
|
||||
// symbolization and pretty-print the markup. See the spec at:
|
||||
// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
|
||||
// See the spec at:
|
||||
// https://llvm.org/docs/SymbolizerMarkupFormat.html
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// This is used by UBSan for type names, and by ASan for global variable names.
|
||||
// It's expected to return a static buffer that will be reused on each call.
|
||||
const char *Symbolizer::Demangle(const char *name) {
|
||||
static char buffer[kFormatDemangleMax];
|
||||
internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
|
||||
return buffer;
|
||||
#include "sanitizer_symbolizer_markup.h"
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
#include "sanitizer_symbolizer_markup_constants.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void MarkupStackTracePrinter::RenderData(InternalScopedString *buffer,
|
||||
const char *format, const DataInfo *DI,
|
||||
const char *strip_path_prefix) {
|
||||
RenderContext(buffer);
|
||||
buffer->AppendF(kFormatData, reinterpret_cast<void *>(DI->start));
|
||||
}
|
||||
|
||||
// This is used mostly for suppression matching. Making it work
|
||||
// would enable "interceptor_via_lib" suppressions. It's also used
|
||||
// once in UBSan to say "in module ..." in a message that also
|
||||
// includes an address in the module, so post-processing can already
|
||||
// pretty-print that so as to indicate the module.
|
||||
bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
|
||||
uptr *module_address) {
|
||||
bool MarkupStackTracePrinter::RenderNeedsSymbolization(const char *format) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is mainly used by hwasan for online symbolization. This isn't needed
|
||||
// since hwasan can always just dump stack frames for offline symbolization.
|
||||
bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; }
|
||||
|
||||
// This is used in some places for suppression checking, which we
|
||||
// don't really support for Fuchsia. It's also used in UBSan to
|
||||
// identify a PC location to a function name, so we always fill in
|
||||
// the function member with a string containing markup around the PC
|
||||
// value.
|
||||
// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
|
||||
// to render stack frames, but that should be changed to use
|
||||
// RenderStackFrame.
|
||||
SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
|
||||
SymbolizedStack *s = SymbolizedStack::New(addr);
|
||||
char buffer[kFormatFunctionMax];
|
||||
internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr);
|
||||
s->info.function = internal_strdup(buffer);
|
||||
return s;
|
||||
// We don't support the stack_trace_format flag at all.
|
||||
void MarkupStackTracePrinter::RenderFrame(InternalScopedString *buffer,
|
||||
const char *format, int frame_no,
|
||||
uptr address, const AddressInfo *info,
|
||||
bool vs_style,
|
||||
const char *strip_path_prefix) {
|
||||
CHECK(!RenderNeedsSymbolization(format));
|
||||
RenderContext(buffer);
|
||||
buffer->AppendF(kFormatFrame, frame_no, reinterpret_cast<void *>(address));
|
||||
}
|
||||
|
||||
// Always claim we succeeded, so that RenderDataInfo will be called.
|
||||
bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
|
||||
bool MarkupSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *stack) {
|
||||
char buffer[kFormatFunctionMax];
|
||||
internal_snprintf(buffer, sizeof(buffer), kFormatFunction,
|
||||
reinterpret_cast<void *>(addr));
|
||||
stack->info.function = internal_strdup(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MarkupSymbolizerTool::SymbolizeData(uptr addr, DataInfo *info) {
|
||||
info->Clear();
|
||||
info->start = addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We ignore the format argument to __sanitizer_symbolize_global.
|
||||
void RenderData(InternalScopedString *buffer, const char *format,
|
||||
const DataInfo *DI, const char *strip_path_prefix) {
|
||||
buffer->append(kFormatData, DI->start);
|
||||
const char *MarkupSymbolizerTool::Demangle(const char *name) {
|
||||
static char buffer[kFormatDemangleMax];
|
||||
internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool RenderNeedsSymbolization(const char *format) { return false; }
|
||||
// Fuchsia's implementation of symbolizer markup doesn't need to emit contextual
|
||||
// elements at this point.
|
||||
// Fuchsia's logging infrastructure emits enough information about
|
||||
// process memory layout that a post-processing filter can do the
|
||||
// symbolization and pretty-print the markup.
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
// We don't support the stack_trace_format flag at all.
|
||||
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
||||
uptr address, const AddressInfo *info, bool vs_style,
|
||||
const char *strip_path_prefix) {
|
||||
CHECK(!RenderNeedsSymbolization(format));
|
||||
buffer->append(kFormatFrame, frame_no, address);
|
||||
static bool ModulesEq(const LoadedModule &module,
|
||||
const RenderedModule &renderedModule) {
|
||||
return module.base_address() == renderedModule.base_address &&
|
||||
internal_memcmp(module.uuid(), renderedModule.uuid,
|
||||
module.uuid_size()) == 0 &&
|
||||
internal_strcmp(module.full_name(), renderedModule.full_name) == 0;
|
||||
}
|
||||
|
||||
Symbolizer *Symbolizer::PlatformInit() {
|
||||
return new (symbolizer_allocator_) Symbolizer({});
|
||||
static bool ModuleHasBeenRendered(
|
||||
const LoadedModule &module,
|
||||
const InternalMmapVectorNoCtor<RenderedModule> &renderedModules) {
|
||||
for (const auto &renderedModule : renderedModules)
|
||||
if (ModulesEq(module, renderedModule))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
|
||||
static void RenderModule(InternalScopedString *buffer,
|
||||
const LoadedModule &module, uptr moduleId) {
|
||||
InternalScopedString buildIdBuffer;
|
||||
for (uptr i = 0; i < module.uuid_size(); i++)
|
||||
buildIdBuffer.AppendF("%02x", module.uuid()[i]);
|
||||
|
||||
void StartReportDeadlySignal() {}
|
||||
void ReportDeadlySignal(const SignalContext &sig, u32 tid,
|
||||
UnwindSignalStackCallbackType unwind,
|
||||
const void *unwind_context) {}
|
||||
|
||||
#if SANITIZER_CAN_SLOW_UNWIND
|
||||
struct UnwindTraceArg {
|
||||
BufferedStackTrace *stack;
|
||||
u32 max_depth;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
|
||||
UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param);
|
||||
CHECK_LT(arg->stack->size, arg->max_depth);
|
||||
uptr pc = _Unwind_GetIP(ctx);
|
||||
if (pc < PAGE_SIZE) return _URC_NORMAL_STOP;
|
||||
arg->stack->trace_buffer[arg->stack->size++] = pc;
|
||||
return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP
|
||||
: _URC_NO_REASON);
|
||||
buffer->AppendF(kFormatModule, moduleId, module.full_name(),
|
||||
buildIdBuffer.data());
|
||||
buffer->Append("\n");
|
||||
}
|
||||
|
||||
void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
|
||||
CHECK_GE(max_depth, 2);
|
||||
size = 0;
|
||||
UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
|
||||
_Unwind_Backtrace(Unwind_Trace, &arg);
|
||||
CHECK_GT(size, 0);
|
||||
// We need to pop a few frames so that pc is on top.
|
||||
uptr to_pop = LocatePcInTrace(pc);
|
||||
// trace_buffer[0] belongs to the current function so we always pop it,
|
||||
// unless there is only 1 frame in the stack trace (1 frame is always better
|
||||
// than 0!).
|
||||
PopStackFrames(Min(to_pop, static_cast<uptr>(1)));
|
||||
trace_buffer[0] = pc;
|
||||
static void RenderMmaps(InternalScopedString *buffer,
|
||||
const LoadedModule &module, uptr moduleId) {
|
||||
InternalScopedString accessBuffer;
|
||||
|
||||
// All module mmaps are readable at least
|
||||
for (const auto &range : module.ranges()) {
|
||||
accessBuffer.Append("r");
|
||||
if (range.writable)
|
||||
accessBuffer.Append("w");
|
||||
if (range.executable)
|
||||
accessBuffer.Append("x");
|
||||
|
||||
//{{{mmap:%starting_addr:%size_in_hex:load:%moduleId:r%(w|x):%relative_addr}}}
|
||||
|
||||
// module.base_address == dlpi_addr
|
||||
// range.beg == dlpi_addr + p_vaddr
|
||||
// relative address == p_vaddr == range.beg - module.base_address
|
||||
buffer->AppendF(kFormatMmap, reinterpret_cast<void *>(range.beg),
|
||||
range.end - range.beg, static_cast<int>(moduleId),
|
||||
accessBuffer.data(), range.beg - module.base_address());
|
||||
|
||||
buffer->Append("\n");
|
||||
accessBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
|
||||
CHECK(context);
|
||||
CHECK_GE(max_depth, 2);
|
||||
UNREACHABLE("signal context doesn't exist");
|
||||
void MarkupStackTracePrinter::RenderContext(InternalScopedString *buffer) {
|
||||
if (renderedModules_.size() == 0)
|
||||
buffer->Append("{{{reset}}}\n");
|
||||
|
||||
const auto &modules = Symbolizer::GetOrInit()->GetRefreshedListOfModules();
|
||||
|
||||
for (const auto &module : modules) {
|
||||
if (ModuleHasBeenRendered(module, renderedModules_))
|
||||
continue;
|
||||
|
||||
// symbolizer markup id, used to refer to this modules from other contextual
|
||||
// elements
|
||||
uptr moduleId = renderedModules_.size();
|
||||
|
||||
RenderModule(buffer, module, moduleId);
|
||||
RenderMmaps(buffer, module, moduleId);
|
||||
|
||||
renderedModules_.push_back({
|
||||
internal_strdup(module.full_name()),
|
||||
module.base_address(),
|
||||
{},
|
||||
});
|
||||
|
||||
// kModuleUUIDSize is the size of curModule.uuid
|
||||
CHECK_GE(kModuleUUIDSize, module.uuid_size());
|
||||
internal_memcpy(renderedModules_.back().uuid, module.uuid(),
|
||||
module.uuid_size());
|
||||
}
|
||||
#endif // SANITIZER_CAN_SLOW_UNWIND
|
||||
}
|
||||
#endif // !SANITIZER_FUCHSIA
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_MARKUP
|
||||
|
||||
79
lib/tsan/sanitizer_common/sanitizer_symbolizer_markup.h
vendored
Normal file
79
lib/tsan/sanitizer_common/sanitizer_symbolizer_markup.h
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
//===-- sanitizer_symbolizer_markup.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 various sanitizers' runtime libraries.
|
||||
//
|
||||
// Header for the offline markup symbolizer.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_SYMBOLIZER_MARKUP_H
|
||||
#define SANITIZER_SYMBOLIZER_MARKUP_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_stacktrace_printer.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
#include "sanitizer_symbolizer_internal.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Simplier view of a LoadedModule. It only holds information necessary to
|
||||
// identify unique modules.
|
||||
struct RenderedModule {
|
||||
char *full_name;
|
||||
uptr base_address;
|
||||
u8 uuid[kModuleUUIDSize]; // BuildId
|
||||
};
|
||||
|
||||
class MarkupStackTracePrinter : public StackTracePrinter {
|
||||
public:
|
||||
// We don't support the stack_trace_format flag at all.
|
||||
void RenderFrame(InternalScopedString *buffer, const char *format,
|
||||
int frame_no, uptr address, const AddressInfo *info,
|
||||
bool vs_style, const char *strip_path_prefix = "") override;
|
||||
|
||||
bool RenderNeedsSymbolization(const char *format) override;
|
||||
|
||||
// We ignore the format argument to __sanitizer_symbolize_global.
|
||||
void RenderData(InternalScopedString *buffer, const char *format,
|
||||
const DataInfo *DI,
|
||||
const char *strip_path_prefix = "") override;
|
||||
|
||||
private:
|
||||
// Keeps track of the modules that have been rendered to avoid re-rendering
|
||||
// them
|
||||
InternalMmapVector<RenderedModule> renderedModules_;
|
||||
void RenderContext(InternalScopedString *buffer);
|
||||
|
||||
protected:
|
||||
~MarkupStackTracePrinter() {}
|
||||
};
|
||||
|
||||
class MarkupSymbolizerTool final : public SymbolizerTool {
|
||||
public:
|
||||
// This is used in some places for suppression checking, which we
|
||||
// don't really support for Fuchsia. It's also used in UBSan to
|
||||
// identify a PC location to a function name, so we always fill in
|
||||
// the function member with a string containing markup around the PC
|
||||
// value.
|
||||
// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
|
||||
// to render stack frames, but that should be changed to use
|
||||
// RenderStackFrame.
|
||||
bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
|
||||
|
||||
// Always claim we succeeded, so that RenderDataInfo will be called.
|
||||
bool SymbolizeData(uptr addr, DataInfo *info) override;
|
||||
|
||||
// May return NULL if demangling failed.
|
||||
// This is used by UBSan for type names, and by ASan for global variable
|
||||
// names. It's expected to return a static buffer that will be reused on each
|
||||
// call.
|
||||
const char *Demangle(const char *name) override;
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_MARKUP_H
|
||||
@ -1,4 +1,5 @@
|
||||
//===-- sanitizer_symbolizer_fuchsia.h -----------------------------------===//
|
||||
//===-- sanitizer_symbolizer_markup_constants.h
|
||||
//-----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
@ -8,10 +9,10 @@
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries.
|
||||
//
|
||||
// Define Fuchsia's string formats and limits for the markup symbolizer.
|
||||
// Define string formats and limits for the markup symbolizer.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_SYMBOLIZER_FUCHSIA_H
|
||||
#define SANITIZER_SYMBOLIZER_FUCHSIA_H
|
||||
#ifndef SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H
|
||||
#define SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
@ -32,11 +33,17 @@ constexpr uptr kFormatFunctionMax = 64; // More than big enough for 64-bit hex.
|
||||
constexpr const char *kFormatData = "{{{data:%p}}}";
|
||||
|
||||
// One frame in a backtrace (printed on a line by itself).
|
||||
constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}";
|
||||
constexpr const char *kFormatFrame = "{{{bt:%d:%p}}}";
|
||||
|
||||
// Module contextual element.
|
||||
constexpr const char *kFormatModule = "{{{module:%zu:%s:elf:%s}}}";
|
||||
|
||||
// mmap for a module segment.
|
||||
constexpr const char *kFormatMmap = "{{{mmap:%p:0x%zx:load:%d:%s:0x%zx}}}";
|
||||
|
||||
// Dump trigger element.
|
||||
#define FORMAT_DUMPFILE "{{{dumpfile:%s:%s}}}"
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_FUCHSIA_H
|
||||
#endif // SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H
|
||||
85
lib/tsan/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp
vendored
Normal file
85
lib/tsan/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
//===-- sanitizer_symbolizer_markup_fuchsia.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 is shared between various sanitizers' runtime libraries.
|
||||
//
|
||||
// Fuchsia specific implementation of offline markup symbolizer.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_SYMBOLIZER_MARKUP
|
||||
|
||||
# include "sanitizer_common.h"
|
||||
# include "sanitizer_stacktrace_printer.h"
|
||||
# include "sanitizer_symbolizer.h"
|
||||
# include "sanitizer_symbolizer_markup.h"
|
||||
# include "sanitizer_symbolizer_markup_constants.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// This is used by UBSan for type names, and by ASan for global variable names.
|
||||
// It's expected to return a static buffer that will be reused on each call.
|
||||
const char *Symbolizer::Demangle(const char *name) {
|
||||
static char buffer[kFormatDemangleMax];
|
||||
internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// This is used mostly for suppression matching. Making it work
|
||||
// would enable "interceptor_via_lib" suppressions. It's also used
|
||||
// once in UBSan to say "in module ..." in a message that also
|
||||
// includes an address in the module, so post-processing can already
|
||||
// pretty-print that so as to indicate the module.
|
||||
bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
|
||||
uptr *module_address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is mainly used by hwasan for online symbolization. This isn't needed
|
||||
// since hwasan can always just dump stack frames for offline symbolization.
|
||||
bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; }
|
||||
|
||||
// This is used in some places for suppression checking, which we
|
||||
// don't really support for Fuchsia. It's also used in UBSan to
|
||||
// identify a PC location to a function name, so we always fill in
|
||||
// the function member with a string containing markup around the PC
|
||||
// value.
|
||||
// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
|
||||
// to render stack frames, but that should be changed to use
|
||||
// RenderStackFrame.
|
||||
SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
|
||||
SymbolizedStack *s = SymbolizedStack::New(addr);
|
||||
char buffer[kFormatFunctionMax];
|
||||
internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr);
|
||||
s->info.function = internal_strdup(buffer);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Always claim we succeeded, so that RenderDataInfo will be called.
|
||||
bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
|
||||
info->Clear();
|
||||
info->start = addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fuchsia only uses MarkupStackTracePrinter
|
||||
StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
|
||||
return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
|
||||
}
|
||||
|
||||
void MarkupStackTracePrinter::RenderContext(InternalScopedString *) {}
|
||||
|
||||
Symbolizer *Symbolizer::PlatformInit() {
|
||||
return new (symbolizer_allocator_) Symbolizer({});
|
||||
}
|
||||
|
||||
void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_MARKUP
|
||||
@ -12,6 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#include "sanitizer_symbolizer_markup.h"
|
||||
#if SANITIZER_POSIX
|
||||
# include <dlfcn.h> // for dlsym()
|
||||
# include <errno.h>
|
||||
@ -56,7 +57,7 @@ const char *DemangleCXXABI(const char *name) {
|
||||
__cxxabiv1::__cxa_demangle(name, 0, 0, 0))
|
||||
return demangled_name;
|
||||
|
||||
return name;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// As of now, there are no headers for the Swift runtime. Once they are
|
||||
@ -324,9 +325,12 @@ __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool
|
||||
__sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,
|
||||
char *Buffer, int MaxLength);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool
|
||||
__sanitizer_symbolize_frame(const char *ModuleName, u64 ModuleOffset,
|
||||
char *Buffer, int MaxLength);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_symbolize_flush();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool
|
||||
__sanitizer_symbolize_demangle(const char *Name, char *Buffer, int MaxLength);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool
|
||||
__sanitizer_symbolize_set_demangle(bool Demangle);
|
||||
@ -337,19 +341,19 @@ __sanitizer_symbolize_set_inline_frames(bool InlineFrames);
|
||||
class InternalSymbolizer final : public SymbolizerTool {
|
||||
public:
|
||||
static InternalSymbolizer *get(LowLevelAllocator *alloc) {
|
||||
if (__sanitizer_symbolize_set_demangle)
|
||||
// These one is the most used one, so we will use it to detect a presence of
|
||||
// internal symbolizer.
|
||||
if (&__sanitizer_symbolize_code == nullptr)
|
||||
return nullptr;
|
||||
CHECK(__sanitizer_symbolize_set_demangle(common_flags()->demangle));
|
||||
if (__sanitizer_symbolize_set_inline_frames)
|
||||
CHECK(__sanitizer_symbolize_set_inline_frames(
|
||||
common_flags()->symbolize_inline_frames));
|
||||
if (__sanitizer_symbolize_code && __sanitizer_symbolize_data)
|
||||
return new (*alloc) InternalSymbolizer();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
|
||||
bool result = __sanitizer_symbolize_code(
|
||||
stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
|
||||
stack->info.module, stack->info.module_offset, buffer_, sizeof(buffer_));
|
||||
if (result)
|
||||
ParseSymbolizePCOutput(buffer_, stack);
|
||||
return result;
|
||||
@ -357,7 +361,7 @@ class InternalSymbolizer final : public SymbolizerTool {
|
||||
|
||||
bool SymbolizeData(uptr addr, DataInfo *info) override {
|
||||
bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
|
||||
buffer_, kBufferSize);
|
||||
buffer_, sizeof(buffer_));
|
||||
if (result) {
|
||||
ParseSymbolizeDataOutput(buffer_, info);
|
||||
info->start += (addr - info->module_offset); // Add the base address.
|
||||
@ -365,34 +369,29 @@ class InternalSymbolizer final : public SymbolizerTool {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
if (__sanitizer_symbolize_flush)
|
||||
__sanitizer_symbolize_flush();
|
||||
bool SymbolizeFrame(uptr addr, FrameInfo *info) override {
|
||||
bool result = __sanitizer_symbolize_frame(info->module, info->module_offset,
|
||||
buffer_, sizeof(buffer_));
|
||||
if (result)
|
||||
ParseSymbolizeFrameOutput(buffer_, &info->locals);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Flush() override { __sanitizer_symbolize_flush(); }
|
||||
|
||||
const char *Demangle(const char *name) override {
|
||||
if (__sanitizer_symbolize_demangle) {
|
||||
for (uptr res_length = 1024;
|
||||
res_length <= InternalSizeClassMap::kMaxSize;) {
|
||||
char *res_buff = static_cast<char *>(InternalAlloc(res_length));
|
||||
uptr req_length =
|
||||
__sanitizer_symbolize_demangle(name, res_buff, res_length);
|
||||
if (req_length > res_length) {
|
||||
res_length = req_length + 1;
|
||||
InternalFree(res_buff);
|
||||
continue;
|
||||
}
|
||||
if (__sanitizer_symbolize_demangle(name, buffer_, sizeof(buffer_))) {
|
||||
char *res_buff = nullptr;
|
||||
ExtractToken(buffer_, "", &res_buff);
|
||||
return res_buff;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
InternalSymbolizer() {}
|
||||
|
||||
static const int kBufferSize = 16 * 1024;
|
||||
char buffer_[kBufferSize];
|
||||
char buffer_[16 * 1024];
|
||||
};
|
||||
# else // SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
|
||||
@ -470,6 +469,12 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
|
||||
VReport(2, "Symbolizer is disabled.\n");
|
||||
return;
|
||||
}
|
||||
if (common_flags()->enable_symbolizer_markup) {
|
||||
VReport(2, "Using symbolizer markup");
|
||||
SymbolizerTool *tool = new (*allocator) MarkupSymbolizerTool();
|
||||
CHECK(tool);
|
||||
list->push_back(tool);
|
||||
}
|
||||
if (IsAllocatorOutOfMemory()) {
|
||||
VReport(2, "Cannot use internal symbolizer: out of memory\n");
|
||||
} else if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
|
||||
|
||||
@ -28,14 +28,41 @@
|
||||
namespace __sanitizer {
|
||||
|
||||
#if !SANITIZER_GO
|
||||
|
||||
static bool FrameIsInternal(const SymbolizedStack *frame) {
|
||||
if (!frame)
|
||||
return true;
|
||||
const char *file = frame->info.file;
|
||||
const char *module = frame->info.module;
|
||||
// On Gentoo, the path is g++-*, so there's *not* a missing /.
|
||||
if (file && (internal_strstr(file, "/compiler-rt/lib/") ||
|
||||
internal_strstr(file, "/include/c++/") ||
|
||||
internal_strstr(file, "/include/g++")))
|
||||
return true;
|
||||
if (file && internal_strstr(file, "\\compiler-rt\\lib\\"))
|
||||
return true;
|
||||
if (module && (internal_strstr(module, "libclang_rt.")))
|
||||
return true;
|
||||
if (module && (internal_strstr(module, "clang_rt.")))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) {
|
||||
for (const SymbolizedStack *f = frames; f; f = f->next)
|
||||
if (!FrameIsInternal(f))
|
||||
return f;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ReportErrorSummary(const char *error_type, const AddressInfo &info,
|
||||
const char *alt_tool_name) {
|
||||
if (!common_flags()->print_summary) return;
|
||||
InternalScopedString buff;
|
||||
buff.append("%s ", error_type);
|
||||
RenderFrame(&buff, "%L %F", 0, info.address, &info,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
buff.AppendF("%s ", error_type);
|
||||
StackTracePrinter::GetOrInit()->RenderFrame(
|
||||
&buff, "%L %F", 0, info.address, &info,
|
||||
common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix);
|
||||
ReportErrorSummary(buff.data(), alt_tool_name);
|
||||
}
|
||||
#endif
|
||||
@ -75,16 +102,33 @@ void ReportErrorSummary(const char *error_type, const StackTrace *stack,
|
||||
#if !SANITIZER_GO
|
||||
if (!common_flags()->print_summary)
|
||||
return;
|
||||
if (stack->size == 0) {
|
||||
ReportErrorSummary(error_type);
|
||||
|
||||
// Find first non-internal stack frame.
|
||||
for (uptr i = 0; i < stack->size; ++i) {
|
||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[i]);
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
Symbolizer::GetOrInit()->SymbolizePC(pc));
|
||||
if (const SymbolizedStack *frame = symbolized_stack.get()) {
|
||||
if (const SymbolizedStack *summary_frame = SkipInternalFrames(frame)) {
|
||||
ReportErrorSummary(error_type, summary_frame->info, alt_tool_name);
|
||||
return;
|
||||
}
|
||||
// Currently, we include the first stack frame into the report summary.
|
||||
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to the top one.
|
||||
if (stack->size) {
|
||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
||||
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
Symbolizer::GetOrInit()->SymbolizePC(pc));
|
||||
if (const SymbolizedStack *frame = symbolized_stack.get()) {
|
||||
ReportErrorSummary(error_type, frame->info, alt_tool_name);
|
||||
frame->ClearAll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to a summary without location.
|
||||
ReportErrorSummary(error_type);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -148,7 +192,7 @@ static void MaybeReportNonExecRegion(uptr pc) {
|
||||
static void PrintMemoryByte(InternalScopedString *str, const char *before,
|
||||
u8 byte) {
|
||||
SanitizerCommonDecorator d;
|
||||
str->append("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15,
|
||||
str->AppendF("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15,
|
||||
d.Default());
|
||||
}
|
||||
|
||||
@ -156,14 +200,14 @@ static void MaybeDumpInstructionBytes(uptr pc) {
|
||||
if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
|
||||
return;
|
||||
InternalScopedString str;
|
||||
str.append("First 16 instruction bytes at pc: ");
|
||||
str.AppendF("First 16 instruction bytes at pc: ");
|
||||
if (IsAccessibleMemoryRange(pc, 16)) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
PrintMemoryByte(&str, "", ((u8 *)pc)[i]);
|
||||
}
|
||||
str.append("\n");
|
||||
str.AppendF("\n");
|
||||
} else {
|
||||
str.append("unaccessible\n");
|
||||
str.AppendF("unaccessible\n");
|
||||
}
|
||||
Report("%s", str.data());
|
||||
}
|
||||
|
||||
33
lib/tsan/sanitizer_common/sanitizer_symbolizer_report_fuchsia.cpp
vendored
Normal file
33
lib/tsan/sanitizer_common/sanitizer_symbolizer_report_fuchsia.cpp
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
//===-- sanitizer_symbolizer_report_fuchsia.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implementation of the report functions for fuchsia.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_SYMBOLIZER_MARKUP
|
||||
|
||||
# include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
void StartReportDeadlySignal() {}
|
||||
|
||||
void ReportDeadlySignal(const SignalContext &sig, u32 tid,
|
||||
UnwindSignalStackCallbackType unwind,
|
||||
const void *unwind_context) {}
|
||||
|
||||
void HandleDeadlySignal(void *siginfo, void *context, u32 tid,
|
||||
UnwindSignalStackCallbackType unwind,
|
||||
const void *unwind_context) {}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_MARKUP
|
||||
@ -175,9 +175,7 @@ const char *WinSymbolizerTool::Demangle(const char *name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *Symbolizer::PlatformDemangle(const char *name) {
|
||||
return name;
|
||||
}
|
||||
const char *Symbolizer::PlatformDemangle(const char *name) { return nullptr; }
|
||||
|
||||
namespace {
|
||||
struct ScopedHandle {
|
||||
@ -233,7 +231,7 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() {
|
||||
CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
|
||||
CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
|
||||
"args ending in backslash and empty args unsupported");
|
||||
command_line.append("\"%s\" ", arg);
|
||||
command_line.AppendF("\"%s\" ", arg);
|
||||
}
|
||||
VReport(3, "Launching symbolizer command: %s\n", command_line.data());
|
||||
|
||||
|
||||
131
lib/tsan/sanitizer_common/sanitizer_syscall_linux_hexagon.inc
vendored
Normal file
131
lib/tsan/sanitizer_common/sanitizer_syscall_linux_hexagon.inc
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
//===-- sanitizer_syscall_linux_hexagon.inc ---------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implementations of internal_syscall and internal_iserror for Linux/hexagon.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define SYSCALL(name) __NR_##name
|
||||
|
||||
#define __internal_syscall_LL_E(x) \
|
||||
((union { \
|
||||
long long ll; \
|
||||
long l[2]; \
|
||||
}){.ll = x}) \
|
||||
.l[0], \
|
||||
((union { \
|
||||
long long ll; \
|
||||
long l[2]; \
|
||||
}){.ll = x}) \
|
||||
.l[1]
|
||||
#define __internal_syscall_LL_O(x) 0, __SYSCALL_LL_E((x))
|
||||
|
||||
#define __asm_syscall(...) \
|
||||
do { \
|
||||
__asm__ __volatile__("trap0(#1)" : "=r"(r0) : __VA_ARGS__ : "memory"); \
|
||||
return r0; \
|
||||
} while (0)
|
||||
|
||||
#define __internal_syscall0(n) (__internal_syscall)(n)
|
||||
|
||||
static uptr __internal_syscall(long n) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0");
|
||||
__asm_syscall("r"(r6));
|
||||
}
|
||||
|
||||
#define __internal_syscall1(n, a1) (__internal_syscall)(n, (long)(a1))
|
||||
|
||||
static uptr __internal_syscall(long n, long a) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0") = a;
|
||||
__asm_syscall("r"(r6), "0"(r0));
|
||||
}
|
||||
|
||||
#define __internal_syscall2(n, a1, a2) \
|
||||
(__internal_syscall)(n, (long)(a1), (long)(a2))
|
||||
|
||||
static uptr __internal_syscall(long n, long a, long b) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0") = a;
|
||||
register u32 r1 __asm__("r1") = b;
|
||||
__asm_syscall("r"(r6), "0"(r0), "r"(r1));
|
||||
}
|
||||
|
||||
#define __internal_syscall3(n, a1, a2, a3) \
|
||||
(__internal_syscall)(n, (long)(a1), (long)(a2), (long)(a3))
|
||||
|
||||
static uptr __internal_syscall(long n, long a, long b, long c) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0") = a;
|
||||
register u32 r1 __asm__("r1") = b;
|
||||
register u32 r2 __asm__("r2") = c;
|
||||
__asm_syscall("r"(r6), "0"(r0), "r"(r1), "r"(r2));
|
||||
}
|
||||
|
||||
#define __internal_syscall4(n, a1, a2, a3, a4) \
|
||||
(__internal_syscall)(n, (long)(a1), (long)(a2), (long)(a3), (long)(a4))
|
||||
|
||||
static uptr __internal_syscall(long n, long a, long b, long c, long d) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0") = a;
|
||||
register u32 r1 __asm__("r1") = b;
|
||||
register u32 r2 __asm__("r2") = c;
|
||||
register u32 r3 __asm__("r3") = d;
|
||||
__asm_syscall("r"(r6), "0"(r0), "r"(r1), "r"(r2), "r"(r3));
|
||||
}
|
||||
|
||||
#define __internal_syscall5(n, a1, a2, a3, a4, a5) \
|
||||
(__internal_syscall)(n, (long)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(long)(a5))
|
||||
|
||||
static uptr __internal_syscall(long n, long a, long b, long c, long d, long e) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0") = a;
|
||||
register u32 r1 __asm__("r1") = b;
|
||||
register u32 r2 __asm__("r2") = c;
|
||||
register u32 r3 __asm__("r3") = d;
|
||||
register u32 r4 __asm__("r4") = e;
|
||||
__asm_syscall("r"(r6), "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4));
|
||||
}
|
||||
|
||||
#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \
|
||||
(__internal_syscall)(n, (long)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(long)(a5), (long)(a6))
|
||||
|
||||
static uptr __internal_syscall(long n, long a, long b, long c, long d, long e,
|
||||
long f) {
|
||||
register u32 r6 __asm__("r6") = n;
|
||||
register u32 r0 __asm__("r0") = a;
|
||||
register u32 r1 __asm__("r1") = b;
|
||||
register u32 r2 __asm__("r2") = c;
|
||||
register u32 r3 __asm__("r3") = d;
|
||||
register u32 r4 __asm__("r4") = e;
|
||||
register u32 r5 __asm__("r5") = f;
|
||||
__asm_syscall("r"(r6), "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5));
|
||||
}
|
||||
|
||||
#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n
|
||||
#define __SYSCALL_NARGS(...) \
|
||||
__SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, )
|
||||
#define __SYSCALL_CONCAT_X(a, b) a##b
|
||||
#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b)
|
||||
#define __SYSCALL_DISP(b, ...) \
|
||||
__SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
|
||||
|
||||
// Helper function used to avoid clobbering of errno.
|
||||
bool internal_iserror(uptr retval, int *rverrno) {
|
||||
if (retval >= (uptr)-4095) {
|
||||
if (rverrno)
|
||||
*rverrno = -retval;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
171
lib/tsan/sanitizer_common/sanitizer_syscall_linux_loongarch64.inc
vendored
Normal file
171
lib/tsan/sanitizer_common/sanitizer_syscall_linux_loongarch64.inc
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
//===-- sanitizer_syscall_linux_loongarch64.inc -----------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implementations of internal_syscall and internal_iserror for
|
||||
// Linux/loongarch64.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// About local register variables:
|
||||
// https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html#Local-Register-Variables
|
||||
//
|
||||
// Kernel ABI:
|
||||
// https://lore.kernel.org/loongarch/1f353678-3398-e30b-1c87-6edb278f74db@xen0n.name/T/#m1613bc86c2d7bf5f6da92bd62984302bfd699a2f
|
||||
// syscall number is placed in a7
|
||||
// parameters, if present, are placed in a0-a6
|
||||
// upon return:
|
||||
// the return value is placed in a0
|
||||
// t0-t8 should be considered clobbered
|
||||
// all other registers are preserved
|
||||
#define SYSCALL(name) __NR_##name
|
||||
|
||||
#define INTERNAL_SYSCALL_CLOBBERS \
|
||||
"memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8"
|
||||
|
||||
static uptr __internal_syscall(u64 nr) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0");
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "=r"(a0)
|
||||
: "r"(a7)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall0(n) (__internal_syscall)(n)
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall1(n, a1) (__internal_syscall)(n, (u64)(a1))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
register u64 a1 asm("$a1") = arg2;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall2(n, a1, a2) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
register u64 a1 asm("$a1") = arg2;
|
||||
register u64 a2 asm("$a2") = arg3;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall3(n, a1, a2, a3) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
|
||||
u64 arg4) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
register u64 a1 asm("$a1") = arg2;
|
||||
register u64 a2 asm("$a2") = arg3;
|
||||
register u64 a3 asm("$a3") = arg4;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall4(n, a1, a2, a3, a4) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4,
|
||||
long arg5) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
register u64 a1 asm("$a1") = arg2;
|
||||
register u64 a2 asm("$a2") = arg3;
|
||||
register u64 a3 asm("$a3") = arg4;
|
||||
register u64 a4 asm("$a4") = arg5;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall5(n, a1, a2, a3, a4, a5) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(u64)(a5))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4,
|
||||
long arg5, long arg6) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
register u64 a1 asm("$a1") = arg2;
|
||||
register u64 a2 asm("$a2") = arg3;
|
||||
register u64 a3 asm("$a3") = arg4;
|
||||
register u64 a4 asm("$a4") = arg5;
|
||||
register u64 a5 asm("$a5") = arg6;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(u64)(a5), (long)(a6))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4,
|
||||
long arg5, long arg6, long arg7) {
|
||||
register u64 a7 asm("$a7") = nr;
|
||||
register u64 a0 asm("$a0") = arg1;
|
||||
register u64 a1 asm("$a1") = arg2;
|
||||
register u64 a2 asm("$a2") = arg3;
|
||||
register u64 a3 asm("$a3") = arg4;
|
||||
register u64 a4 asm("$a4") = arg5;
|
||||
register u64 a5 asm("$a5") = arg6;
|
||||
register u64 a6 asm("$a6") = arg7;
|
||||
__asm__ volatile("syscall 0\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
|
||||
"r"(a6)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall7(n, a1, a2, a3, a4, a5, a6, a7) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(u64)(a5), (long)(a6), (long)(a7))
|
||||
|
||||
#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n
|
||||
#define __SYSCALL_NARGS(...) \
|
||||
__SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, )
|
||||
#define __SYSCALL_CONCAT_X(a, b) a##b
|
||||
#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b)
|
||||
#define __SYSCALL_DISP(b, ...) \
|
||||
__SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
|
||||
|
||||
// Helper function used to avoid clobbering of errno.
|
||||
bool internal_iserror(uptr retval, int *internal_errno) {
|
||||
if (retval >= (uptr)-4095) {
|
||||
if (internal_errno)
|
||||
*internal_errno = -retval;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
174
lib/tsan/sanitizer_common/sanitizer_syscall_linux_riscv64.inc
vendored
Normal file
174
lib/tsan/sanitizer_common/sanitizer_syscall_linux_riscv64.inc
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
//===-- sanitizer_syscall_linux_riscv64.inc ---------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implementations of internal_syscall and internal_iserror for Linux/riscv64.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// About local register variables:
|
||||
// https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html#Local-Register-Variables
|
||||
//
|
||||
// Kernel ABI...
|
||||
// To my surprise I haven't found much information regarding it.
|
||||
// Kernel source and internet browsing shows that:
|
||||
// syscall number is passed in a7
|
||||
// (http://man7.org/linux/man-pages/man2/syscall.2.html) results are return in
|
||||
// a0 and a1 (http://man7.org/linux/man-pages/man2/syscall.2.html) arguments
|
||||
// are passed in: a0-a7 (see below)
|
||||
//
|
||||
// Regarding the arguments. The only "documentation" I could find is
|
||||
// this comment (!!!) by Bruce Hold on google forums (!!!):
|
||||
// https://groups.google.com/a/groups.riscv.org/forum/#!topic/sw-dev/exbrzM3GZDQ
|
||||
// Confirmed by inspecting glibc sources.
|
||||
// Great way to document things.
|
||||
#define SYSCALL(name) __NR_##name
|
||||
|
||||
#define INTERNAL_SYSCALL_CLOBBERS "memory"
|
||||
|
||||
static uptr __internal_syscall(u64 nr) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0");
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "=r"(a0)
|
||||
: "r"(a7)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall0(n) (__internal_syscall)(n)
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall1(n, a1) (__internal_syscall)(n, (u64)(a1))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
register u64 a1 asm("a1") = arg2;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall2(n, a1, a2) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
register u64 a1 asm("a1") = arg2;
|
||||
register u64 a2 asm("a2") = arg3;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall3(n, a1, a2, a3) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
|
||||
u64 arg4) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
register u64 a1 asm("a1") = arg2;
|
||||
register u64 a2 asm("a2") = arg3;
|
||||
register u64 a3 asm("a3") = arg4;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall4(n, a1, a2, a3, a4) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4,
|
||||
long arg5) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
register u64 a1 asm("a1") = arg2;
|
||||
register u64 a2 asm("a2") = arg3;
|
||||
register u64 a3 asm("a3") = arg4;
|
||||
register u64 a4 asm("a4") = arg5;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall5(n, a1, a2, a3, a4, a5) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(u64)(a5))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4,
|
||||
long arg5, long arg6) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
register u64 a1 asm("a1") = arg2;
|
||||
register u64 a2 asm("a2") = arg3;
|
||||
register u64 a3 asm("a3") = arg4;
|
||||
register u64 a4 asm("a4") = arg5;
|
||||
register u64 a5 asm("a5") = arg6;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(u64)(a5), (long)(a6))
|
||||
|
||||
static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, u64 arg4,
|
||||
long arg5, long arg6, long arg7) {
|
||||
register u64 a7 asm("a7") = nr;
|
||||
register u64 a0 asm("a0") = arg1;
|
||||
register u64 a1 asm("a1") = arg2;
|
||||
register u64 a2 asm("a2") = arg3;
|
||||
register u64 a3 asm("a3") = arg4;
|
||||
register u64 a4 asm("a4") = arg5;
|
||||
register u64 a5 asm("a5") = arg6;
|
||||
register u64 a6 asm("a6") = arg7;
|
||||
__asm__ volatile("ecall\n\t"
|
||||
: "+r"(a0)
|
||||
: "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
|
||||
"r"(a6)
|
||||
: INTERNAL_SYSCALL_CLOBBERS);
|
||||
return a0;
|
||||
}
|
||||
#define __internal_syscall7(n, a1, a2, a3, a4, a5, a6, a7) \
|
||||
(__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
|
||||
(u64)(a5), (long)(a6), (long)(a7))
|
||||
|
||||
#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n
|
||||
#define __SYSCALL_NARGS(...) \
|
||||
__SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, )
|
||||
#define __SYSCALL_CONCAT_X(a, b) a##b
|
||||
#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b)
|
||||
#define __SYSCALL_DISP(b, ...) \
|
||||
__SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
|
||||
|
||||
// Helper function used to avoid clobbering of errno.
|
||||
bool internal_iserror(uptr retval, int *rverrno) {
|
||||
if (retval >= (uptr)-4095) {
|
||||
if (rverrno)
|
||||
*rverrno = -retval;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -23,6 +23,9 @@ void ThreadArgRetval::CreateLocked(uptr thread, bool detached,
|
||||
Data& t = data_[thread];
|
||||
t = {};
|
||||
t.gen = gen_++;
|
||||
static_assert(sizeof(gen_) == sizeof(u32) && kInvalidGen == UINT32_MAX);
|
||||
if (gen_ == kInvalidGen)
|
||||
gen_ = 0;
|
||||
t.detached = detached;
|
||||
t.args = args;
|
||||
}
|
||||
@ -53,16 +56,28 @@ void ThreadArgRetval::Finish(uptr thread, void* retval) {
|
||||
u32 ThreadArgRetval::BeforeJoin(uptr thread) const {
|
||||
__sanitizer::Lock lock(&mtx_);
|
||||
auto t = data_.find(thread);
|
||||
CHECK(t);
|
||||
CHECK(!t->second.detached);
|
||||
if (t && !t->second.detached) {
|
||||
return t->second.gen;
|
||||
}
|
||||
if (!common_flags()->detect_invalid_join)
|
||||
return kInvalidGen;
|
||||
const char* reason = "unknown";
|
||||
if (!t) {
|
||||
reason = "already joined";
|
||||
} else if (t->second.detached) {
|
||||
reason = "detached";
|
||||
}
|
||||
Report("ERROR: %s: Joining %s thread, aborting.\n", SanitizerToolName,
|
||||
reason);
|
||||
Die();
|
||||
}
|
||||
|
||||
void ThreadArgRetval::AfterJoin(uptr thread, u32 gen) {
|
||||
__sanitizer::Lock lock(&mtx_);
|
||||
auto t = data_.find(thread);
|
||||
if (!t || gen != t->second.gen) {
|
||||
// Thread was reused and erased by any other event.
|
||||
// Thread was reused and erased by any other event, or we had an invalid
|
||||
// join.
|
||||
return;
|
||||
}
|
||||
CHECK(!t->second.detached);
|
||||
|
||||
@ -93,6 +93,7 @@ class SANITIZER_MUTEX ThreadArgRetval {
|
||||
// will keep pointers alive forever, missing leaks caused by cancelation.
|
||||
|
||||
private:
|
||||
static const u32 kInvalidGen = UINT32_MAX;
|
||||
struct Data {
|
||||
Args args;
|
||||
u32 gen; // Avoid collision if thread id re-used.
|
||||
|
||||
@ -121,25 +121,26 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
|
||||
uptr tls_size = 0;
|
||||
uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
|
||||
VReport(2,
|
||||
"__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: 0x%zx; sp: %p "
|
||||
"__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: %p; sp: %p "
|
||||
"num_live_dtls %zd\n",
|
||||
(void *)arg, arg->dso_id, arg->offset, res, tls_beg, (void *)&tls_beg,
|
||||
(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={0x%zx,0x%zx}\n",
|
||||
tls_beg, tls_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) {
|
||||
// This is the static TLS block which was initialized / unpoisoned at thread
|
||||
// creation.
|
||||
VReport(2, "__tls_get_addr: static tls: 0x%zx\n", tls_beg);
|
||||
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={0x%zx,0x%zx}\n",
|
||||
tls_beg, tls_size);
|
||||
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.
|
||||
|
||||
66
lib/tsan/sanitizer_common/sanitizer_unwind_fuchsia.cpp
vendored
Normal file
66
lib/tsan/sanitizer_common/sanitizer_unwind_fuchsia.cpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
//===------------------ sanitizer_unwind_fuchsia.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// Sanitizer unwind Fuchsia specific functions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_FUCHSIA
|
||||
|
||||
# include <limits.h>
|
||||
# include <unwind.h>
|
||||
|
||||
# include "sanitizer_common.h"
|
||||
# include "sanitizer_stacktrace.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
# if SANITIZER_CAN_SLOW_UNWIND
|
||||
struct UnwindTraceArg {
|
||||
BufferedStackTrace *stack;
|
||||
u32 max_depth;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
|
||||
UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param);
|
||||
CHECK_LT(arg->stack->size, arg->max_depth);
|
||||
uptr pc = _Unwind_GetIP(ctx);
|
||||
if (pc < GetPageSizeCached())
|
||||
return _URC_NORMAL_STOP;
|
||||
arg->stack->trace_buffer[arg->stack->size++] = pc;
|
||||
return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP
|
||||
: _URC_NO_REASON);
|
||||
}
|
||||
|
||||
void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
|
||||
CHECK_GE(max_depth, 2);
|
||||
size = 0;
|
||||
UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
|
||||
_Unwind_Backtrace(Unwind_Trace, &arg);
|
||||
CHECK_GT(size, 0);
|
||||
// We need to pop a few frames so that pc is on top.
|
||||
uptr to_pop = LocatePcInTrace(pc);
|
||||
// trace_buffer[0] belongs to the current function so we always pop it,
|
||||
// unless there is only 1 frame in the stack trace (1 frame is always better
|
||||
// than 0!).
|
||||
PopStackFrames(Min(to_pop, static_cast<uptr>(1)));
|
||||
trace_buffer[0] = pc;
|
||||
}
|
||||
|
||||
void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
|
||||
CHECK(context);
|
||||
CHECK_GE(max_depth, 2);
|
||||
UNREACHABLE("signal context doesn't exist");
|
||||
}
|
||||
# endif // SANITIZER_CAN_SLOW_UNWIND
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FUCHSIA
|
||||
@ -69,11 +69,18 @@ void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
|
||||
stack_frame.AddrFrame.Offset = ctx.Rbp;
|
||||
stack_frame.AddrStack.Offset = ctx.Rsp;
|
||||
# endif
|
||||
# else
|
||||
# if SANITIZER_ARM
|
||||
int machine_type = IMAGE_FILE_MACHINE_ARM;
|
||||
stack_frame.AddrPC.Offset = ctx.Pc;
|
||||
stack_frame.AddrFrame.Offset = ctx.R11;
|
||||
stack_frame.AddrStack.Offset = ctx.Sp;
|
||||
# else
|
||||
int machine_type = IMAGE_FILE_MACHINE_I386;
|
||||
stack_frame.AddrPC.Offset = ctx.Eip;
|
||||
stack_frame.AddrFrame.Offset = ctx.Ebp;
|
||||
stack_frame.AddrStack.Offset = ctx.Esp;
|
||||
# endif
|
||||
# endif
|
||||
stack_frame.AddrPC.Mode = AddrModeFlat;
|
||||
stack_frame.AddrFrame.Mode = AddrModeFlat;
|
||||
|
||||
25
lib/tsan/sanitizer_common/sanitizer_win.cpp
vendored
25
lib/tsan/sanitizer_common/sanitizer_win.cpp
vendored
@ -144,7 +144,7 @@ void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
void UnmapOrDie(void *addr, uptr size) {
|
||||
void UnmapOrDie(void *addr, uptr size, bool raw_report) {
|
||||
if (!size || !addr)
|
||||
return;
|
||||
|
||||
@ -156,10 +156,7 @@ void UnmapOrDie(void *addr, uptr size) {
|
||||
// fails try MEM_DECOMMIT.
|
||||
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
|
||||
if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
|
||||
Report("ERROR: %s failed to "
|
||||
"deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
|
||||
SanitizerToolName, size, size, addr, GetLastError());
|
||||
CHECK("unable to unmap" && 0);
|
||||
ReportMunmapFailureAndDie(addr, size, GetLastError(), raw_report);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,8 +276,8 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) {
|
||||
MEM_COMMIT, PAGE_READWRITE);
|
||||
if (p == 0) {
|
||||
char mem_type[30];
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
|
||||
fixed_addr);
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p",
|
||||
(void *)fixed_addr);
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
|
||||
}
|
||||
return p;
|
||||
@ -311,8 +308,8 @@ void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) {
|
||||
MEM_COMMIT, PAGE_READWRITE);
|
||||
if (p == 0) {
|
||||
char mem_type[30];
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
|
||||
fixed_addr);
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p",
|
||||
(void *)fixed_addr);
|
||||
return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate");
|
||||
}
|
||||
return p;
|
||||
@ -387,9 +384,8 @@ bool DontDumpShadowMemory(uptr addr, uptr length) {
|
||||
}
|
||||
|
||||
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
|
||||
uptr min_shadow_base_alignment,
|
||||
UNUSED uptr &high_mem_end) {
|
||||
const uptr granularity = GetMmapGranularity();
|
||||
uptr min_shadow_base_alignment, UNUSED uptr &high_mem_end,
|
||||
uptr granularity) {
|
||||
const uptr alignment =
|
||||
Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
|
||||
const uptr left_padding =
|
||||
@ -995,10 +991,15 @@ void SignalContext::InitPcSpBp() {
|
||||
bp = (uptr)context_record->Rbp;
|
||||
sp = (uptr)context_record->Rsp;
|
||||
# endif
|
||||
# else
|
||||
# if SANITIZER_ARM
|
||||
bp = (uptr)context_record->R11;
|
||||
sp = (uptr)context_record->Sp;
|
||||
# else
|
||||
bp = (uptr)context_record->Ebp;
|
||||
sp = (uptr)context_record->Esp;
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
uptr SignalContext::GetAddress() const {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user