diff --git a/lib/libtsan/builtins/assembly.h b/lib/libtsan/builtins/assembly.h new file mode 100644 index 0000000000000000000000000000000000000000..34c71241524dc9997101e2d76e347f0cb024795e --- /dev/null +++ b/lib/libtsan/builtins/assembly.h @@ -0,0 +1,305 @@ +//===-- 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() +#include +#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 + +#ifdef __arm__ +#include "int_endianness.h" + +#if _YUGA_BIG_ENDIAN +#define VMOV_TO_DOUBLE(dst, src0, src1) vmov dst, src1, src0 SEPARATOR +#define VMOV_FROM_DOUBLE(dst0, dst1, src) vmov dst1, dst0, src SEPARATOR +#else +#define VMOV_TO_DOUBLE(dst, src0, src1) vmov dst, src0, src1 SEPARATOR +#define VMOV_FROM_DOUBLE(dst0, dst1, src) vmov dst0, dst1, src SEPARATOR +#endif +#endif + +#endif // COMPILERRT_ASSEMBLY_H diff --git a/lib/libtsan/interception/interception.h b/lib/libtsan/interception/interception.h new file mode 100644 index 0000000000000000000000000000000000000000..3cb6b446638e0a6480f1894a5f4ad92d1f5819f6 --- /dev/null +++ b/lib/libtsan/interception/interception.h @@ -0,0 +1,391 @@ +//===-- interception.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Machinery for providing replacements/wrappers for system functions. +//===----------------------------------------------------------------------===// + +#ifndef INTERCEPTION_H +#define INTERCEPTION_H + +#include "sanitizer_common/sanitizer_asm.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_APPLE && \ + !SANITIZER_NETBSD && !SANITIZER_WINDOWS && !SANITIZER_FUCHSIA && \ + !SANITIZER_SOLARIS +# error "Interception doesn't work on this operating system." +#endif + +// These typedefs should be used only in the interceptor definitions to replace +// the standard system types (e.g. SSIZE_T instead of ssize_t) +// On Windows the system headers (basetsd.h) provide a conflicting definition +// of SIZE_T/SSIZE_T that do not match the real size_t/ssize_t for 32-bit +// systems (using long instead of the expected int). Work around the typedef +// redefinition by #defining SIZE_T instead of using a typedef. +// TODO: We should be using __sanitizer::usize (and a new ssize) instead of +// these new macros as long as we ensure they match the real system definitions. +#if SANITIZER_WINDOWS +// Ensure that (S)SIZE_T were already defined as we are about to override them. +# include +#endif + +#define SIZE_T __sanitizer::usize +#define SSIZE_T __sanitizer::ssize +typedef __sanitizer::sptr PTRDIFF_T; +typedef __sanitizer::s64 INTMAX_T; +typedef __sanitizer::u64 UINTMAX_T; +typedef __sanitizer::OFF_T OFF_T; +typedef __sanitizer::OFF64_T OFF64_T; + +// How to add an interceptor: +// Suppose you need to wrap/replace system function (generally, from libc): +// int foo(const char *bar, double baz); +// You'll need to: +// 1) define INTERCEPTOR(int, foo, const char *bar, double baz) { ... } in +// your source file. See the notes below for cases when +// INTERCEPTOR_WITH_SUFFIX(...) should be used instead. +// 2) Call "INTERCEPT_FUNCTION(foo)" prior to the first call of "foo". +// INTERCEPT_FUNCTION(foo) evaluates to "true" iff the function was +// intercepted successfully. +// You can access original function by calling REAL(foo)(bar, baz). +// By default, REAL(foo) will be visible only inside your interceptor, and if +// you want to use it in other parts of RTL, you'll need to: +// 3a) add DECLARE_REAL(int, foo, const char*, double) to a +// header file. +// However, if the call "INTERCEPT_FUNCTION(foo)" and definition for +// INTERCEPTOR(..., foo, ...) are in different files, you'll instead need to: +// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double) +// to a header file. + +// Notes: 1. Things may not work properly if macro INTERCEPTOR(...) {...} or +// DECLARE_REAL(...) are located inside namespaces. +// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo)" to +// effectively redirect calls from "foo" to "zoo". In this case +// you aren't required to implement +// INTERCEPTOR(int, foo, const char *bar, double baz) {...} +// but instead you'll have to add +// DECLARE_REAL(int, foo, const char *bar, double baz) in your +// source file (to define a pointer to overriden function). +// 3. Some Mac functions have symbol variants discriminated by +// additional suffixes, e.g. _$UNIX2003 (see +// https://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/index.html +// for more details). To intercept such functions you need to use the +// INTERCEPTOR_WITH_SUFFIX(...) macro. + +// How it works on Linux +// --------------------- +// +// To replace system functions on Linux we just need to declare functions with +// the same names in our library and then obtain the real function pointers +// using dlsym(). +// +// There is one complication: a user may also intercept some of the functions we +// intercept. To allow for up to 3 interceptors (including ours) of a given +// function "func", the interceptor implementation is in ___interceptor_func, +// which is aliased by a weak function __interceptor_func, which in turn is +// aliased (via a trampoline) by weak wrapper function "func". +// +// Most user interceptors should define a foreign interceptor as follows: +// +// - provide a non-weak function "func" that performs interception; +// - if __interceptor_func exists, call it to perform the real functionality; +// - if it does not exist, figure out the real function and call it instead. +// +// In rare cases, a foreign interceptor (of another dynamic analysis runtime) +// may be defined as follows (on supported architectures): +// +// - provide a non-weak function __interceptor_func that performs interception; +// - if ___interceptor_func exists, call it to perform the real functionality; +// - if it does not exist, figure out the real function and call it instead; +// - provide a weak function "func" that is an alias to __interceptor_func. +// +// With this protocol, sanitizer interceptors, foreign user interceptors, and +// foreign interceptors of other dynamic analysis runtimes, or any combination +// thereof, may co-exist simultaneously. +// +// How it works on Mac OS +// ---------------------- +// +// This is not so on Mac OS, where the two-level namespace makes our replacement +// functions invisible to other libraries. This may be overcomed using the +// DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared libraries in +// Chromium were noticed when doing so. +// +// Instead we create a dylib containing a __DATA,__interpose section that +// associates library functions with their wrappers. When this dylib is +// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all the +// calls to interposed functions done through stubs to the wrapper functions. +// +// As it's decided at compile time which functions are to be intercepted on Mac, +// INTERCEPT_FUNCTION() is effectively a no-op on this system. + +#if SANITIZER_APPLE +#include // For __DARWIN_ALIAS_C(). + +// Just a pair of pointers. +struct interpose_substitution { + const __sanitizer::uptr replacement; + const __sanitizer::uptr original; +}; + +// For a function foo() create a global pair of pointers { wrap_foo, foo } in +// the __DATA,__interpose section. +// As a result all the calls to foo() will be routed to wrap_foo() at runtime. +#define INTERPOSER(func_name) __attribute__((used)) \ +const interpose_substitution substitution_##func_name[] \ + __attribute__((section("__DATA, __interpose"))) = { \ + { reinterpret_cast(WRAP(func_name)), \ + reinterpret_cast(func_name) } \ +} + +// For a function foo() and a wrapper function bar() create a global pair +// of pointers { bar, foo } in the __DATA,__interpose section. +// As a result all the calls to foo() will be routed to bar() at runtime. +#define INTERPOSER_2(func_name, wrapper_name) __attribute__((used)) \ +const interpose_substitution substitution_##func_name[] \ + __attribute__((section("__DATA, __interpose"))) = { \ + { reinterpret_cast(wrapper_name), \ + reinterpret_cast(func_name) } \ +} + +# define WRAP(x) wrap_##x +# define TRAMPOLINE(x) WRAP(x) +# define INTERCEPTOR_ATTRIBUTE +# define DECLARE_WRAPPER(ret_type, func, ...) + +#elif SANITIZER_WINDOWS +# define WRAP(x) __asan_wrap_##x +# define TRAMPOLINE(x) WRAP(x) +# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport) +# define DECLARE_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__); +# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \ + extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__); +#elif !SANITIZER_FUCHSIA // LINUX, FREEBSD, NETBSD, SOLARIS +# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) +# if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT +// Weak aliases of weak aliases do not work, therefore we need to set up a +// trampoline function. The function "func" is a weak alias to the trampoline +// (so that we may check if "func" was overridden), which calls the weak +// function __interceptor_func, which in turn aliases the actual interceptor +// implementation ___interceptor_func: +// +// [wrapper "func": weak] --(alias)--> [TRAMPOLINE(func)] +// | +// +--------(tail call)-------+ +// | +// v +// [__interceptor_func: weak] --(alias)--> [WRAP(func)] +// +// We use inline assembly to define most of this, because not all compilers +// support functions with the "naked" attribute with every architecture. +# define WRAP(x) ___interceptor_ ## x +# define TRAMPOLINE(x) __interceptor_trampoline_ ## x +# if SANITIZER_FREEBSD || SANITIZER_NETBSD +// FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher +// priority than weak ones so weak aliases won't work for indirect calls +// in position-independent (-fPIC / -fPIE) mode. +# define __ASM_WEAK_WRAPPER(func) ".globl " #func "\n" +# 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__); \ + extern "C" ret_type TRAMPOLINE(func)(__VA_ARGS__); \ + extern "C" ret_type __interceptor_##func(__VA_ARGS__) \ + INTERCEPTOR_ATTRIBUTE __attribute__((weak)) ALIAS(WRAP(func)); \ + asm( \ + ".text\n" \ + __ASM_WEAK_WRAPPER(func) \ + ".set " #func ", " SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n" \ + ".globl " SANITIZER_STRINGIFY(TRAMPOLINE(func)) "\n" \ + ".type " SANITIZER_STRINGIFY(TRAMPOLINE(func)) ", " \ + ASM_TYPE_FUNCTION_STR "\n" \ + SANITIZER_STRINGIFY(TRAMPOLINE(func)) ":\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" \ + ); +# else // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT +// Some architectures cannot implement efficient interceptor trampolines with +// just a plain jump due to complexities of resolving a preemptible symbol. In +// those cases, revert to just this scheme: +// +// [wrapper "func": weak] --(alias)--> [WRAP(func)] +// +# define WRAP(x) __interceptor_ ## x +# define TRAMPOLINE(x) WRAP(x) +# if SANITIZER_FREEBSD || SANITIZER_NETBSD +# define __ATTRIBUTE_WEAK_WRAPPER +# else +# define __ATTRIBUTE_WEAK_WRAPPER __attribute__((weak)) +# endif // SANITIZER_FREEBSD || SANITIZER_NETBSD +# define DECLARE_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__) \ + INTERCEPTOR_ATTRIBUTE __ATTRIBUTE_WEAK_WRAPPER ALIAS(WRAP(func)); +# endif // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT +#endif + +#if SANITIZER_FUCHSIA +// There is no general interception at all on Fuchsia. +// Sanitizer runtimes just define functions directly to preempt them, +// and have bespoke ways to access the underlying libc functions. +# include +# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) +# define REAL(x) __unsanitized_##x +# define DECLARE_REAL(ret_type, func, ...) +#elif !SANITIZER_APPLE +# define PTR_TO_REAL(x) real_##x +# define REAL(x) __interception::PTR_TO_REAL(x) +# define FUNC_TYPE(x) x##_type + +# define DECLARE_REAL(ret_type, func, ...) \ + typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ + namespace __interception { \ + extern FUNC_TYPE(func) PTR_TO_REAL(func); \ + } +# define ASSIGN_REAL(dst, src) REAL(dst) = REAL(src) +#else // SANITIZER_APPLE +# define REAL(x) x +# define DECLARE_REAL(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__); +# define ASSIGN_REAL(x, y) +#endif // SANITIZER_APPLE + +#if !SANITIZER_FUCHSIA +# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ + DECLARE_REAL(ret_type, func, __VA_ARGS__) \ + extern "C" ret_type TRAMPOLINE(func)(__VA_ARGS__); \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); +// Declare an interceptor and its wrapper defined in a different translation +// unit (ex. asm). +# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type TRAMPOLINE(func)(__VA_ARGS__); \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); \ + extern "C" ret_type func(__VA_ARGS__); +#else +# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) +# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) +#endif + +// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR +// macros does its job. In exceptional cases you may need to call REAL(foo) +// without defining INTERCEPTOR(..., foo, ...). For example, if you override +// foo with an interceptor for other function. +#if !SANITIZER_APPLE && !SANITIZER_FUCHSIA +# define DEFINE_REAL(ret_type, func, ...) \ + typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ + namespace __interception { \ + FUNC_TYPE(func) PTR_TO_REAL(func); \ + } +#else +# define DEFINE_REAL(ret_type, func, ...) +#endif + +#if SANITIZER_FUCHSIA + +// We need to define the __interceptor_func name just to get +// sanitizer_common/scripts/gen_dynamic_list.py to export func. +// But we don't need to export __interceptor_func to get that. +#define INTERCEPTOR(ret_type, func, ...) \ + extern "C"[[ gnu::alias(#func), gnu::visibility("hidden") ]] ret_type \ + __interceptor_##func(__VA_ARGS__); \ + extern "C" INTERCEPTOR_ATTRIBUTE ret_type func(__VA_ARGS__) + +#elif !SANITIZER_APPLE + +#define INTERCEPTOR(ret_type, func, ...) \ + DEFINE_REAL(ret_type, func, __VA_ARGS__) \ + DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ + extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__) + +// We don't need INTERCEPTOR_WITH_SUFFIX on non-Darwin for now. +#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ + INTERCEPTOR(ret_type, func, __VA_ARGS__) + +#else // SANITIZER_APPLE + +#define INTERCEPTOR_ZZZ(suffix, ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__) suffix; \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); \ + INTERPOSER(func); \ + extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__) + +#define INTERCEPTOR(ret_type, func, ...) \ + INTERCEPTOR_ZZZ(/*no symbol variants*/, ret_type, func, __VA_ARGS__) + +#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ + INTERCEPTOR_ZZZ(__DARWIN_ALIAS_C(func), ret_type, func, __VA_ARGS__) + +// Override |overridee| with |overrider|. +#define OVERRIDE_FUNCTION(overridee, overrider) \ + INTERPOSER_2(overridee, WRAP(overrider)) +#endif + +#if SANITIZER_WINDOWS +# define INTERCEPTOR_WINAPI(ret_type, func, ...) \ + typedef ret_type (__stdcall *FUNC_TYPE(func))(__VA_ARGS__); \ + namespace __interception { \ + FUNC_TYPE(func) PTR_TO_REAL(func); \ + } \ + extern "C" INTERCEPTOR_ATTRIBUTE ret_type __stdcall WRAP(func)(__VA_ARGS__) +#endif + +// ISO C++ forbids casting between pointer-to-function and pointer-to-object, +// so we use casts via uintptr_t (the local __sanitizer::uptr equivalent). +namespace __interception { + +#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 + +#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS + +# include "interception_linux.h" +# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) +#elif SANITIZER_APPLE +# include "interception_mac.h" +# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_MAC(func, symver) +#elif SANITIZER_WINDOWS +# include "interception_win.h" +# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_WIN(func, symver) +#endif + +#undef INCLUDED_FROM_INTERCEPTION_LIB + +#endif // INTERCEPTION_H diff --git a/lib/libtsan/interception/interception_linux.cpp b/lib/libtsan/interception/interception_linux.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef8136eb4fc70132346c521b0ef2084b9cb806c4 --- /dev/null +++ b/lib/libtsan/interception/interception_linux.cpp @@ -0,0 +1,83 @@ +//===-- interception_linux.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific interception methods. +//===----------------------------------------------------------------------===// + +#include "interception.h" + +#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS + +#include // for dlsym() and dlvsym() + +namespace __interception { + +#if SANITIZER_NETBSD +static int StrCmp(const char *s1, const char *s2) { + while (true) { + if (*s1 != *s2) + return false; + if (*s1 == 0) + return true; + s1++; + s2++; + } +} +#endif + +static void *GetFuncAddr(const char *name, uptr trampoline) { +#if SANITIZER_NETBSD + // FIXME: Find a better way to handle renames + if (StrCmp(name, "sigaction")) + name = "__sigaction14"; +#endif + void *addr = dlsym(RTLD_NEXT, name); + if (!addr) { + // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is + // later in the library search order than the DSO that we are trying to + // intercept, which means that we cannot intercept this function. We still + // want the address of the real definition, though, so look it up using + // RTLD_DEFAULT. + addr = dlsym(RTLD_DEFAULT, name); + + // In case `name' is not loaded, dlsym ends up finding the actual wrapper. + // We don't want to intercept the wrapper and have it point to itself. + if ((uptr)addr == trampoline) + addr = nullptr; + } + return addr; +} + +bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func, + uptr trampoline) { + void *addr = GetFuncAddr(name, trampoline); + *ptr_to_real = (uptr)addr; + return addr && (func == trampoline); +} + +// dlvsym is a GNU extension supported by some other platforms. +#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD +static void *GetFuncAddr(const char *name, const char *ver) { + return dlvsym(RTLD_NEXT, name, ver); +} + +bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, + uptr func, uptr trampoline) { + void *addr = GetFuncAddr(name, ver); + *ptr_to_real = (uptr)addr; + return addr && (func == trampoline); +} +# endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD + +} // namespace __interception + +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || + // SANITIZER_SOLARIS diff --git a/lib/libtsan/interception/interception_linux.h b/lib/libtsan/interception/interception_linux.h new file mode 100644 index 0000000000000000000000000000000000000000..2e01ff44578c3ce42b2d554aef6a8fac7f974acb --- /dev/null +++ b/lib/libtsan/interception/interception_linux.h @@ -0,0 +1,55 @@ +//===-- interception_linux.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 AddressSanitizer, an address sanity checker. +// +// Linux-specific interception methods. +//===----------------------------------------------------------------------===// + +#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS + +#if !defined(INCLUDED_FROM_INTERCEPTION_LIB) +# error interception_linux.h should be included from interception library only +#endif + +#ifndef INTERCEPTION_LINUX_H +#define INTERCEPTION_LINUX_H + +namespace __interception { +bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func, + uptr trampoline); +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)(decltype(REAL(func)))&(func), \ + (::__interception::uptr) &TRAMPOLINE(func)) + +// dlvsym is a GNU extension supported by some other platforms. +#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD +#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ + ::__interception::InterceptFunction( \ + #func, symver, \ + (::__interception::uptr *)&REAL(func), \ + (::__interception::uptr)(decltype(REAL(func)))&(func), \ + (::__interception::uptr)&TRAMPOLINE(func)) +#else +#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ + INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) +#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD + +#endif // INTERCEPTION_LINUX_H +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || + // SANITIZER_SOLARIS diff --git a/lib/libtsan/interception/interception_mac.cpp b/lib/libtsan/interception/interception_mac.cpp new file mode 100644 index 0000000000000000000000000000000000000000..03eae0fdca0d80cd86bc6ee79690afac747cb8e0 --- /dev/null +++ b/lib/libtsan/interception/interception_mac.cpp @@ -0,0 +1,18 @@ +//===-- interception_mac.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific interception methods. +//===----------------------------------------------------------------------===// + +#include "interception.h" + +#if SANITIZER_APPLE + +#endif // SANITIZER_APPLE diff --git a/lib/libtsan/interception/interception_mac.h b/lib/libtsan/interception/interception_mac.h new file mode 100644 index 0000000000000000000000000000000000000000..26079518c649b38524e68868b706b722cdd32628 --- /dev/null +++ b/lib/libtsan/interception/interception_mac.h @@ -0,0 +1,27 @@ +//===-- interception_mac.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 AddressSanitizer, an address sanity checker. +// +// Mac-specific interception methods. +//===----------------------------------------------------------------------===// + +#if SANITIZER_APPLE + +#if !defined(INCLUDED_FROM_INTERCEPTION_LIB) +# error "interception_mac.h should be included from interception.h only" +#endif + +#ifndef INTERCEPTION_MAC_H +#define INTERCEPTION_MAC_H + +#define INTERCEPT_FUNCTION_MAC(func) +#define INTERCEPT_FUNCTION_VER_MAC(func, symver) + +#endif // INTERCEPTION_MAC_H +#endif // SANITIZER_APPLE diff --git a/lib/libtsan/interception/interception_type_test.cpp b/lib/libtsan/interception/interception_type_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41041ce6f95c8809b73498e8789ded623c5fb5d3 --- /dev/null +++ b/lib/libtsan/interception/interception_type_test.cpp @@ -0,0 +1,46 @@ +//===-- interception_type_test.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Compile-time tests of the internal type definitions. +//===----------------------------------------------------------------------===// + +#include "interception.h" +#include "sanitizer_common/sanitizer_type_traits.h" + +#if __has_include() +# include +#endif +#include +#include + +COMPILER_CHECK((__sanitizer::is_same<__sanitizer::uptr, ::uintptr_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<__sanitizer::sptr, ::intptr_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<__sanitizer::usize, ::size_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<::PTRDIFF_T, ::ptrdiff_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<::SIZE_T, ::size_t>::value)); +#if !SANITIZER_WINDOWS +// No ssize_t on Windows. +COMPILER_CHECK((__sanitizer::is_same<::SSIZE_T, ::ssize_t>::value)); +#endif +// TODO: These are not actually the same type on Linux (long vs long long) +COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t)); +COMPILER_CHECK(sizeof(::UINTMAX_T) == sizeof(uintmax_t)); + +#if SANITIZER_GLIBC || SANITIZER_ANDROID +COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t)); +#endif + +// The following are the cases when pread (and friends) is used instead of +// pread64. In those cases we need OFF_T to match off_t. We don't care about the +// rest (they depend on _FILE_OFFSET_BITS setting when building an application). +#if !SANITIZER_WINDOWS && (SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \ + _FILE_OFFSET_BITS != 64) +COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t)); +#endif diff --git a/lib/libtsan/interception/interception_win.cpp b/lib/libtsan/interception/interception_win.cpp new file mode 100644 index 0000000000000000000000000000000000000000..002b37468a200c4f2a08321a29775cbb9d96cc94 --- /dev/null +++ b/lib/libtsan/interception/interception_win.cpp @@ -0,0 +1,1443 @@ +//===-- 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. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific interception methods. +// +// This file is implementing several hooking techniques to intercept calls +// to functions. The hooks are dynamically installed by modifying the assembly +// code. +// +// The hooking techniques are making assumptions on the way the code is +// generated and are safe under these assumptions. +// +// On 64-bit architecture, there is no direct 64-bit jump instruction. To allow +// arbitrary branching on the whole memory space, the notion of trampoline +// region is used. A trampoline region is a memory space withing 2G boundary +// where it is safe to add custom assembly code to build 64-bit jumps. +// +// Hooking techniques +// ================== +// +// 1) Detour +// +// The Detour hooking technique is assuming the presence of a header with +// padding and an overridable 2-bytes nop instruction (mov edi, edi). The +// nop instruction can safely be replaced by a 2-bytes jump without any need +// to save the instruction. A jump to the target is encoded in the function +// header and the nop instruction is replaced by a short jump to the header. +// +// head: 5 x nop head: jmp +// func: mov edi, edi --> func: jmp short +// [...] real: [...] +// +// This technique is only implemented on 32-bit architecture. +// Most of the time, Windows API are hookable with the detour technique. +// +// 2) Redirect Jump +// +// The redirect jump is applicable when the first instruction is a direct +// jump. The instruction is replaced by jump to the hook. +// +// func: jmp