tsan: Update to LLVM 19.1.0.

This commit is contained in:
Alex Rønne Petersen 2024-09-24 13:47:29 +02:00 committed by Andrew Kelley
parent 7f6b7c5608
commit a40cdad18c
129 changed files with 4880 additions and 2546 deletions

293
lib/tsan/builtins/assembly.h vendored Normal file
View 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

View File

@ -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

View File

@ -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) \

View File

@ -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) {

View File

@ -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) {

View File

@ -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();

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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;
}

View File

@ -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(); }

View File

@ -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:

View File

@ -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"

View File

@ -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(&current_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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View 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

View 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

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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,

View File

@ -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.")

View File

@ -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());

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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++;

View File

@ -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);

View File

@ -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_));

View File

@ -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.

View File

@ -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

View File

@ -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 =

View File

@ -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"

View File

@ -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");

View File

@ -31,6 +31,10 @@ struct __sanitizer_struct_mallinfo {
int v[10];
};
struct __sanitizer_struct_mallinfo2 {
uptr v[10];
};
#endif
} // namespace __sanitizer

View File

@ -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();
}

View File

@ -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); }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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) \

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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_;
}

View File

@ -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);
}

View File

@ -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() {

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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.
};
};

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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

View 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

View File

@ -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

View 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

View File

@ -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)) {

View File

@ -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());
}

View 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

View File

@ -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());

View 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;
}

View 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;
}

View 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;
}

View File

@ -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);

View File

@ -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.

View File

@ -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.

View 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

View File

@ -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;

View File

@ -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