Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -168,6 +168,7 @@ set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64}) set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64} ${MIPS32} ${MIPS64} ${S390X}) +set(ALL_TYSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64}) set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64}) set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X}) @@ -359,6 +360,9 @@ list_intersect(PROFILE_SUPPORTED_ARCH ALL_PROFILE_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(TYSAN_SUPPORTED_ARCH + ALL_TYSAN_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(TSAN_SUPPORTED_ARCH ALL_TSAN_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) @@ -395,6 +399,7 @@ filter_available_targets(LSAN_SUPPORTED_ARCH ${ALL_LSAN_SUPPORTED_ARCH}) filter_available_targets(MSAN_SUPPORTED_ARCH ${ALL_MSAN_SUPPORTED_ARCH}) filter_available_targets(PROFILE_SUPPORTED_ARCH ${ALL_PROFILE_SUPPORTED_ARCH}) + filter_available_targets(TYSAN_SUPPORTED_ARCH ${ALL_TYSAN_SUPPORTED_ARCH}) filter_available_targets(TSAN_SUPPORTED_ARCH ${ALL_TSAN_SUPPORTED_ARCH}) filter_available_targets(UBSAN_SUPPORTED_ARCH ${ALL_UBSAN_SUPPORTED_ARCH}) filter_available_targets(SAFESTACK_SUPPORTED_ARCH @@ -430,7 +435,7 @@ set(OS_NAME "${CMAKE_SYSTEM_NAME}") endif() -set(ALL_SANITIZERS asan;dfsan;msan;tsan;safestack;cfi;esan;scudo) +set(ALL_SANITIZERS asan;dfsan;msan;tysan;tsan;safestack;cfi;esan;scudo) set(COMPILER_RT_SANITIZERS_TO_BUILD ${ALL_SANITIZERS} CACHE STRING "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})") list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}") @@ -491,6 +496,13 @@ set(COMPILER_RT_HAS_PROFILE FALSE) endif() +if (COMPILER_RT_HAS_SANITIZER_COMMON AND TYSAN_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux") + set(COMPILER_RT_HAS_TYSAN TRUE) +else() + set(COMPILER_RT_HAS_TYSAN FALSE) +endif() + if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND OS_NAME MATCHES "Darwin|Linux|FreeBSD|Android") set(COMPILER_RT_HAS_TSAN TRUE) Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -53,6 +53,7 @@ compiler_rt_build_sanitizer(cfi) compiler_rt_build_sanitizer(esan) compiler_rt_build_sanitizer(scudo) + compiler_rt_build_sanitizer(tysan) compiler_rt_build_runtime(profile) endif() Index: lib/tysan/CMakeLists.txt =================================================================== --- /dev/null +++ lib/tysan/CMakeLists.txt @@ -0,0 +1,33 @@ +include_directories(..) + +# Runtime library sources and build flags. +set(TYSAN_RTL_SOURCES + tysan.cc + tysan_interceptors.cc) +set(TYSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_rtti_flag(OFF TYSAN_COMMON_CFLAGS) +# Prevent clang from generating libc calls. +append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding TYSAN_COMMON_CFLAGS) + +# Static runtime library. +add_compiler_rt_component(tysan) + +foreach(arch ${TYSAN_SUPPORTED_ARCH}) + set(TYSAN_CFLAGS ${TYSAN_COMMON_CFLAGS}) + append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE TYSAN_CFLAGS) + add_compiler_rt_runtime(clang_rt.tysan + STATIC + ARCHS ${arch} + SOURCES ${TYSAN_RTL_SOURCES} + $ + $ + $ + CFLAGS ${TYSAN_CFLAGS} + PARENT_TARGET tysan) + add_sanitizer_rt_symbols(clang_rt.tysan + ARCHS ${arch} + EXTRA tysan.syms.extra) + add_dependencies(tysan + clang_rt.tysan-${arch}-symbols) +endforeach() + Index: lib/tysan/tysan.h =================================================================== --- /dev/null +++ lib/tysan/tysan.h @@ -0,0 +1,84 @@ +//===-- tysan.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of TypeSanitizer. +// +// Private TySan header. +//===----------------------------------------------------------------------===// + +#ifndef TYSAN_H +#define TYSAN_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +using __sanitizer::uptr; +using __sanitizer::u16; + +#include "tysan_platform.h" + +extern "C" { +void tysan_set_type_unknown(void *addr, uptr size); +void tysan_copy_types(void *daddr, void *saddr, uptr size); +} + +namespace __tysan { +extern bool tysan_inited; +extern bool tysan_init_is_running; + +void InitializeInterceptors(); + +enum { + TYSAN_MEMBER_TD = 1, + TYSAN_STRUCT_TD = 2 +}; + +struct tysan_member_type_descriptor { + struct tysan_type_descriptor *Base; + struct tysan_type_descriptor *Access; + uptr Offset; +}; + +struct tysan_struct_type_descriptor { + uptr MemberCount; + struct { + struct tysan_type_descriptor *Type; + uptr Offset; + } Members[1]; // Tail allocated. + // char Name[]; // Tail allocated. +}; + +struct tysan_type_descriptor { + uptr Tag; + union { + tysan_member_type_descriptor Member; + tysan_struct_type_descriptor Struct; + }; +}; + +inline tysan_type_descriptor **shadow_for(void *ptr) { + return (tysan_type_descriptor **) + ((((uptr) ptr) & AppMask())*sizeof(ptr) + ShadowAddr()); +} + +struct Flags { +#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "tysan_flags.inc" +#undef TYSAN_FLAG + + void SetDefaults(); +}; + +extern Flags flags_data; +inline Flags &flags() { + return flags_data; +} + +} // namespace __tysan + +#endif // TYSAN_H Index: lib/tysan/tysan.cc =================================================================== --- /dev/null +++ lib/tysan/tysan.cc @@ -0,0 +1,344 @@ +//===-- tysan.cc --------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of TypeSanitizer. +// +// TypeSanitizer runtime. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +#include "tysan/tysan.h" + +using namespace __sanitizer; +using namespace __tysan; + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void tysan_set_type_unknown(void *addr, uptr size) { + internal_memset(shadow_for(addr), 0, size*sizeof(uptr)); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void tysan_copy_types(void *daddr, void *saddr, uptr size) { + internal_memcpy(shadow_for(daddr), shadow_for(saddr), size*sizeof(uptr)); +} + +static const char *getDisplayName(const char *Name) { + if (Name[0] == '\0') + return ""; + + // Clang generates tags for C++ types that demangle as typeinfo. Remove the + // prefix from the generated string. + const char TIPrefix[] = "typeinfo name for "; + + const char *DName = Symbolizer::GetOrInit()->Demangle(Name); + if (!internal_strncmp(DName, TIPrefix, sizeof(TIPrefix) - 1)) + DName += sizeof(TIPrefix) - 1; + + return DName; +} + +static void printTDName(tysan_type_descriptor *td) { + if (!td) { + Printf(""); + return; + } + + switch (td->Tag) { + default: + DCHECK(0); + break; + case TYSAN_MEMBER_TD: + printTDName(td->Member.Access); + if (td->Member.Access != td->Member.Base) { + Printf(" (in "); + printTDName(td->Member.Base); + Printf(" at offset %u)", td->Member.Offset); + } + break; + case TYSAN_STRUCT_TD: + Printf("%s", getDisplayName((char *) (td->Struct.Members + + td->Struct.MemberCount))); + break; + } +} + +static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) { + tysan_type_descriptor *RootTD = TD; + + do { + RootTD = TD; + + if (TD->Tag == TYSAN_STRUCT_TD) { + if (TD->Struct.MemberCount > 0) + TD = TD->Struct.Members[0].Type; + else + TD = nullptr; + } else if (TD->Tag == TYSAN_MEMBER_TD) { + TD = TD->Member.Access; + } else { + DCHECK(0); + break; + } + } while (TD); + + return RootTD; +} + +static bool isAliasingLegalUp(tysan_type_descriptor *TDA, + tysan_type_descriptor *TDB) { + // Walk up the tree starting with TDA to see if we reach TDB. + uptr OffsetA = 0, OffsetB = 0; + if (TDB->Tag == TYSAN_MEMBER_TD) { + OffsetB = TDB->Member.Offset; + TDB = TDB->Member.Base; + } + + if (TDA->Tag == TYSAN_MEMBER_TD) { + OffsetA = TDA->Member.Offset; + TDA = TDA->Member.Base; + } + + do { + if (TDA == TDB) + return OffsetA == OffsetB; + + if (TDA->Tag == TYSAN_STRUCT_TD) { + if (!TDA->Struct.MemberCount) { + DCHECK(0); + break; + } + + uptr Idx = 0; + for (; Idx < TDA->Struct.MemberCount-1; ++Idx) { + if (TDA->Struct.Members[Idx].Offset >= OffsetA) + break; + } + + OffsetA -= TDA->Struct.Members[Idx].Offset; + TDA = TDA->Struct.Members[Idx].Type; + } else { + DCHECK(0); + break; + } + } while (TDA); + + return false; +} + +static bool isAliasingLegal(tysan_type_descriptor *TDA, + tysan_type_descriptor *TDB) { + if (TDA == TDB || !TDB || !TDA) + return true; + + // Aliasing is legal is the two types have different root nodes. + if (getRootTD(TDA) != getRootTD(TDB)) + return true; + + return isAliasingLegalUp(TDA, TDB) || isAliasingLegalUp(TDB, TDA); +} + +namespace __tysan { +class Decorator: public __sanitizer::SanitizerCommonDecorator { + public: + Decorator() : SanitizerCommonDecorator() { } + const char *Warning() { return Red(); } + const char *Name() { return Green(); } + const char *End() { return Default(); } +}; +} + +ALWAYS_INLINE +static void reportError(void *Addr, int Size, tysan_type_descriptor *TD, + tysan_type_descriptor *OldTD, const char *AccessStr, + const char *DescStr, int Offset, uptr pc, uptr bp, + uptr sp) { + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: TypeSanitizer: type-aliasing-violation on address %p" + " (pc %p bp %p sp %p tid %d)\n", + Addr, (void *) pc, (void *) bp, (void *) sp, GetTid()); + Printf("%s", d.End()); + Printf("%s of size %d at %p with type ", AccessStr, Size, Addr); + + Printf("%s", d.Name()); + printTDName(TD); + Printf("%s", d.End()); + + Printf(" %s of type ", DescStr); + + Printf("%s", d.Name()); + printTDName(OldTD); + Printf("%s", d.End()); + + if (Offset != 0) + Printf(" that starts at offset %d\n", Offset); + else + Printf("\n"); + + if (pc) { + BufferedStackTrace ST; + ST.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false); + ST.Print(); + } else { + Printf("\n"); + } +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __tysan_check(void *addr, int size, tysan_type_descriptor *td, + int flags) { + GET_CALLER_PC_BP_SP; + + bool IsRead = flags & 1; + bool IsWrite = flags & 2; + const char *AccessStr; + if (IsRead && !IsWrite) + AccessStr = "READ"; + else if (!IsRead && IsWrite) + AccessStr = "WRITE"; + else + AccessStr = "ATOMIC UPDATE"; + + tysan_type_descriptor **OldTDPtr = shadow_for(addr); + tysan_type_descriptor *OldTD = *OldTDPtr; + if (((uptr) OldTD) == ~((uptr)0)) { + int i = 0; + do { + --i; + --OldTDPtr; + OldTD = *OldTDPtr; + } while (((uptr) OldTD) == ~((uptr)0)); + + if (!isAliasingLegal(td, OldTD)) + reportError(addr, size, td, OldTD, AccessStr, + "accesses part of an existing object", i, pc, bp, sp); + + return; + } + + if (!isAliasingLegal(td, OldTD)) { + reportError(addr, size, td, OldTD, AccessStr, + "accesses an existing object", 0, pc, bp, sp); + return; + } + + // These types are allowed to alias (or the stored type is unknown), report + // an error if we find an interior type. + + for (int i = 0; i < size; ++i) { + OldTDPtr = shadow_for((void *)(((uptr)addr)+i)); + OldTD = *OldTDPtr; + if (((uptr) OldTD) != ~((uptr)0) && !isAliasingLegal(td, OldTD)) + reportError(addr, size, td, OldTD, AccessStr, + "partially accesses an object", i, pc, bp, sp); + } +} + +Flags __tysan::flags_data; + +SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address; +SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask; + +#ifdef TYSAN_RUNTIME_VMA +// Runtime detected VMA size. +int __tysan::vmaSize; +#endif + +void Flags::SetDefaults() { +#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "tysan_flags.inc" +#undef TYSAN_FLAG +} + +static void RegisterTySanFlags(FlagParser *parser, Flags *f) { +#define TYSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "tysan_flags.inc" +#undef TYSAN_FLAG +} + +static void InitializeFlags() { + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.external_symbolizer_path = GetEnv("TYSAN_SYMBOLIZER_PATH"); + OverrideCommonFlags(cf); + } + + flags().SetDefaults(); + + FlagParser parser; + RegisterCommonFlags(&parser); + RegisterTySanFlags(&parser, &flags()); + parser.ParseString(GetEnv("TYSAN_OPTIONS")); + InitializeCommonFlags(); + if (Verbosity()) ReportUnrecognizedFlags(); + if (common_flags()->help) parser.PrintFlagDescriptions(); +} + +static void InitializePlatformEarly() { + AvoidCVE_2016_2143(); +#ifdef TYSAN_RUNTIME_VMA + vmaSize = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); +#if defined(__aarch64__) + if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) { + Printf("FATAL: TypeSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %d - Supported 39, 42 and 48\n", vmaSize); + Die(); + } +#elif defined(__powerpc64__) + if (vmaSize != 44 && vmaSize != 46) { + Printf("FATAL: TypeSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %d - Supported 44 and 46\n", vmaSize); + Die(); + } +#endif +#endif + + __tysan_shadow_memory_address = ShadowAddr(); + __tysan_app_memory_mask = AppMask(); +} + +namespace __tysan { +bool tysan_inited = false; +bool tysan_init_is_running; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __tysan_init() { + CHECK(!tysan_init_is_running); + if (tysan_inited) return; + tysan_init_is_running = true; + + InitializeFlags(); + InitializePlatformEarly(); + + InitializeInterceptors(); + + MmapFixedNoReserve(ShadowAddr(), AppAddr() - ShadowAddr()); + + tysan_init_is_running = false; + tysan_inited = true; +} + +#if SANITIZER_CAN_USE_PREINIT_ARRAY +__attribute__((section(".preinit_array"), used)) +static void (*tysan_init_ptr)() = __tysan_init; +#endif + Index: lib/tysan/tysan.syms.extra =================================================================== --- /dev/null +++ lib/tysan/tysan.syms.extra @@ -0,0 +1,2 @@ +tysan_* +__tysan_* Index: lib/tysan/tysan_flags.inc =================================================================== --- /dev/null +++ lib/tysan/tysan_flags.inc @@ -0,0 +1,19 @@ +//===-- tysan_flags.inc ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// TySan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef TYSAN_FLAG +# error "Define TYSAN_FLAG prior to including this file!" +#endif + +// TYSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + Index: lib/tysan/tysan_interceptors.cc =================================================================== --- /dev/null +++ lib/tysan/tysan_interceptors.cc @@ -0,0 +1,228 @@ +//===-- tysan_interceptors.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of TypeSanitizer. +// +// Interceptors for standard library functions. +//===----------------------------------------------------------------------===// + +#include "tysan/tysan.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +# define TYSAN_INTERCEPT___STRDUP 1 +#else +# define TYSAN_INTERCEPT___STRDUP 0 +#endif + +#if SANITIZER_LINUX +extern "C" +int mallopt(int param, int value); +#endif + +using namespace __sanitizer; +using namespace __tysan; + +static const uptr early_alloc_buf_size = 16384; +static uptr allocated_bytes; +static char early_alloc_buf[early_alloc_buf_size]; + +static bool isInEarlyAllocBuf(const void *ptr) { + return ((uptr)ptr >= (uptr)early_alloc_buf && + ((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf)); +} + +// Handle allocation requests early (before all interceptors are setup). dlsym, +// for example, calls calloc. +static void *handleEarlyAlloc(uptr size) { + void *mem = (void *)&early_alloc_buf[allocated_bytes]; + allocated_bytes += size; + CHECK_LT(allocated_bytes, early_alloc_buf_size); + return mem; +} + +INTERCEPTOR(void *, memset, void *dst, int v, uptr size) { + if (!tysan_inited && REAL(memset) == nullptr) + return internal_memset(dst, v, size); + + void *res = REAL(memset)(dst, v, size); + tysan_set_type_unknown(dst, size); + return res; +} + +INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) { + if (!tysan_inited && REAL(memmove) == nullptr) + return internal_memmove(dst, src, size); + + void *res = REAL(memmove)(dst, src, size); + tysan_set_type_unknown(dst, size); + return res; +} + +INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) { + if (!tysan_inited && REAL(memcpy) == nullptr) { + // memmove is used here because on some platforms this will also + // intercept the memmove implementation. + return internal_memmove(dst, src, size); + } + + void *res = REAL(memcpy)(dst, src, size); + tysan_set_type_unknown(dst, size); + return res; +} + +INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF_T offset) { + void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + tysan_set_type_unknown(res, RoundUpTo(length, GetPageSize())); + return res; +} + +INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF64_T offset) { + void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + tysan_set_type_unknown(res, RoundUpTo(length, GetPageSize())); + return res; +} + +INTERCEPTOR(char*, strdup, const char *s) { + char *res = REAL(strdup)(s); + if (res) + tysan_copy_types(res, const_cast(s), internal_strlen(s)); + return res; +} + +#if TYSAN_INTERCEPT___STRDUP +INTERCEPTOR(char*, __strdup, const char *s) { + char *res = REAL(__strdup)(s); + if (res) + tysan_copy_types(res, const_cast(s), internal_strlen(s)); + return res; +} +#endif // TYSAN_INTERCEPT___STRDUP + +INTERCEPTOR(void*, malloc, uptr size) { + if (tysan_init_is_running && REAL(malloc) == nullptr) + return handleEarlyAlloc(size); + + void *res = REAL(malloc)(size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(void*, realloc, void *ptr, uptr size) { + void *res = REAL(realloc)(ptr, size); + // We might want to copy the types from the original allocation (although + // that would require that we knew its size). + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (tysan_init_is_running && REAL(calloc) == nullptr) + return handleEarlyAlloc(nmemb*size); + + void *res = REAL(calloc)(nmemb, size); + if (res) + tysan_set_type_unknown(res, nmemb*size); + return res; +} + +INTERCEPTOR(void, free, void *p) { + // There are only a few early allocation requests, + // so we simply skip the free. + if (isInEarlyAllocBuf(p)) + return; + REAL(free)(p); +} + +INTERCEPTOR(void*, valloc, uptr size) { + void *res = REAL(valloc)(size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { + void *res = REAL(memalign)(alignment, size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(void*, __libc_memalign, uptr alignment, uptr size) { + void *res = REAL(__libc_memalign)(alignment, size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(void*, pvalloc, uptr size) { + void *res = REAL(pvalloc)(size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) { + void *res = REAL(aligned_alloc)(alignment, size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} + +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { + int res = REAL(posix_memalign)(memptr, alignment, size); + if (res == 0 && *memptr) + tysan_set_type_unknown(*memptr, size); + return res; +} + +namespace __tysan { +void InitializeInterceptors() { + static int inited = 0; + CHECK_EQ(inited, 0); + + // Instruct libc malloc to consume less memory. +#if SANITIZER_LINUX + mallopt(1, 0); // M_MXFAST + mallopt(-3, 32*1024); // M_MMAP_THRESHOLD +#endif + + INTERCEPT_FUNCTION(mmap); + INTERCEPT_FUNCTION(mmap64); + + INTERCEPT_FUNCTION(strdup); +#if TYSAN_INTERCEPT___STRDUP + INTERCEPT_FUNCTION(__strdup); +#endif + + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(realloc); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(__libc_memalign); + INTERCEPT_FUNCTION(pvalloc); + INTERCEPT_FUNCTION(aligned_alloc); + INTERCEPT_FUNCTION(posix_memalign); + + INTERCEPT_FUNCTION(memset); + INTERCEPT_FUNCTION(memmove); + INTERCEPT_FUNCTION(memcpy); + + inited = 1; +} +} // namespace __tysan Index: lib/tysan/tysan_platform.h =================================================================== --- /dev/null +++ lib/tysan/tysan_platform.h @@ -0,0 +1,125 @@ +//===------------------------ tysan_platform.h ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of TypeSanitizer. +// +// Platform specific information for TySan. +//===----------------------------------------------------------------------===// + +#ifndef TYSAN_PLATFORM_H +#define TYSAN_PLATFORM_H + +namespace __tysan { + +#if defined(__x86_64__) +struct Mapping { + static const uptr kShadowAddr = 0x010000000000ull; + static const uptr kAppAddr = 0x550000000000ull; + static const uptr kAppMemMsk = ~0x780000000000ull; +}; +#elif defined(__mips64) +struct Mapping { + static const uptr kShadowAddr = 0x2400000000ull; + static const uptr kAppAddr = 0xfe00000000ull; + static const uptr kAppMemMsk = ~0xf800000000ull; +}; +#elif defined(__aarch64__) +struct Mapping39 { + static const uptr kShadowAddr = 0x0800000000ull; + static const uptr kAppAddr = 0x5500000000ull; + static const uptr kAppMemMsk = ~0x7800000000ull; +}; + +struct Mapping42 { + static const uptr kShadowAddr = 0x10000000000ull; + static const uptr kAppAddr = 0x2aa00000000ull; + static const uptr kAppMemMsk = ~0x3c000000000ull; +}; + +struct Mapping48 { + static const uptr kShadowAddr = 0x0002000000000ull; + static const uptr kAppAddr = 0x0aaaa00000000ull; + static const uptr kAppMemMsk = ~0x0fff800000000ull; +}; +# define TYSAN_RUNTIME_VMA 1 +#elif defined(__powerpc64__) +struct Mapping44 { + static const uptr kShadowAddr = 0x000100000000ull; + static const uptr kAppAddr = 0x0f0000000000ull; + static const uptr kAppMemMsk = ~0x0f0000000000ull; +}; + +struct Mapping46 { + static const uptr kShadowAddr = 0x010000000000ull; + static const uptr kAppAddr = 0x3d0000000000ull; + static const uptr kAppMemMsk = ~0x3c0000000000ull; +}; +# define TYSAN_RUNTIME_VMA 1 +#else +# error "TySan not supported for this platform!" +#endif + +#if TYSAN_RUNTIME_VMA +extern int vmaSize; +#endif + +enum MappingType { + MAPPING_SHADOW_ADDR, + MAPPING_APP_ADDR, + MAPPING_APP_MASK +}; + +template +uptr MappingImpl(void) { + switch (Type) { + case MAPPING_SHADOW_ADDR: return Mapping::kShadowAddr; + case MAPPING_APP_ADDR: return Mapping::kAppAddr; + case MAPPING_APP_MASK: return Mapping::kAppMemMsk; + } +} + +template +uptr MappingArchImpl(void) { +#ifdef __aarch64__ + switch (vmaSize) { + case 39: return MappingImpl(); + case 42: return MappingImpl(); + case 48: return MappingImpl(); + } + DCHECK(0); + return 0; +#elif defined(__powerpc64__) + if (vmaSize == 44) + return MappingImpl(); + else + return MappingImpl(); + DCHECK(0); +#else + return MappingImpl(); +#endif +} + +ALWAYS_INLINE +uptr ShadowAddr() { + return MappingArchImpl(); +} + +ALWAYS_INLINE +uptr AppAddr() { + return MappingArchImpl(); +} + +ALWAYS_INLINE +uptr AppMask() { + return MappingArchImpl(); +} + +} // namespace __tysan + +#endif Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -65,6 +65,9 @@ if(COMPILER_RT_HAS_TSAN) add_subdirectory(tsan) endif() + if(COMPILER_RT_HAS_TYSAN) + add_subdirectory(tysan) + endif() if(COMPILER_RT_HAS_UBSAN) add_subdirectory(ubsan) endif() Index: test/tysan/CMakeLists.txt =================================================================== --- /dev/null +++ test/tysan/CMakeLists.txt @@ -0,0 +1,32 @@ +set(TYSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(TYSAN_TESTSUITES) + +set(TYSAN_TEST_ARCH ${TYSAN_SUPPORTED_ARCH}) +if(APPLE) + darwin_filter_host_archs(TYSAN_SUPPORTED_ARCH TYSAN_TEST_ARCH) +endif() + +foreach(arch ${TYSAN_TEST_ARCH}) + set(TYSAN_TEST_TARGET_ARCH ${arch}) + string(TOLOWER "-${arch}" TYSAN_TEST_CONFIG_SUFFIX) + get_test_cc_for_arch(${arch} TYSAN_TEST_TARGET_CC TYSAN_TEST_TARGET_CFLAGS) + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG_NAME ${ARCH_UPPER_CASE}Config) + + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) + list(APPEND TYSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) +endforeach() + +set(TYSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND TYSAN_TEST_DEPS tysan) +endif() + +add_lit_testsuite(check-tysan "Running the TypeSanitizer tests" + ${TYSAN_TESTSUITES} + DEPENDS ${TYSAN_TEST_DEPS} + ) +set_target_properties(check-tysan PROPERTIES FOLDER "Compiler-RT Misc") Index: test/tysan/anon-ns.cpp =================================================================== --- /dev/null +++ test/tysan/anon-ns.cpp @@ -0,0 +1,43 @@ +// RUN: %clangxx_tysan -O0 %s -c -o %t.o && \ +// RUN: %clangxx_tysan -O0 %s -DPMAIN -c -o %tm.o && \ +// RUN: %clangxx_tysan -O0 %t.o %tm.o -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include + +// This test demonstrates that the types from anonymous namespaces are +// different in different translation units (while the char* type is the same). + +namespace { +struct X { + X(int i, int j) : a(i), b(j) {} + int a; + int b; +}; +} + +#ifdef PMAIN +void foo(void *context, int i); +char fbyte(void *context); + +int main() { + X x(5, 6); + foo((void *) &x, 8); + std::cout << "fbyte: " << fbyte((void *) &x) << "\n"; +} +#else +void foo(void *context, int i) { + X *x = (X *) context; + x->b = i; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type int (in (anonymous namespace)::X at offset 4) accesses an existing object of type int (in (anonymous namespace)::X at offset 4) +// CHECK: {{#0 0x.* in foo\(void\*, int\) .*anon-ns.cpp:}}[[@LINE-3]] +} + +char fbyte(void *context) { + return *(char *) context; +} +#endif + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + Index: test/tysan/anon-same-struct.c =================================================================== --- /dev/null +++ test/tysan/anon-same-struct.c @@ -0,0 +1,23 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include + +// The two anonymous structs are structurally identical. As a result, we don't +// report an aliasing violation here. +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + +typedef struct { int i1; } s1; +typedef struct { int i2; } s2; + +void f(s1 *s1p, s2 *s2p) { + s1p->i1 = 2; + s2p->i2 = 3; + printf("%i\n", s1p->i1); +} + +int main() { + s1 s = {.i1 = 1}; + f(&s, (s2 *)&s); +} + Index: test/tysan/anon-struct.c =================================================================== --- /dev/null +++ test/tysan/anon-struct.c @@ -0,0 +1,24 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include + +typedef struct { int i1, i1b; } s1; +typedef struct { int i2, i2b, i2c; } s2; + +void f(s1 *s1p, s2 *s2p) { + s1p->i1 = 2; + s2p->i2 = 3; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type int (in at offset 0) accesses an existing object of type int (in at offset 0) +// CHECK: {{#0 0x.* in f .*anon-struct.c:}}[[@LINE-3]] + printf("%i\n", s1p->i1); +} + +int main() { + s1 s = {.i1 = 1, .i1b = 5 }; + f(&s, (s2 *)&s); +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + Index: test/tysan/basic.c =================================================================== --- /dev/null +++ test/tysan/basic.c @@ -0,0 +1,66 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t 10 >%t.out.0 2>&1 +// RUN: FileCheck %s < %t.out.0 +// RUN: %clang_tysan -O2 %s -o %t && %run %t 10 >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include +#include +#include + +void __attribute__((noinline)) add_flt(float *a) { + *a += 2.0f; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: READ of size 4 at {{.*}} with type float accesses an existing object of type int +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-3]] +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type float accesses an existing object of type int +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-6]] +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: READ of size 4 at {{.*}} with type float accesses an existing object of type long +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-9]] +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type float accesses an existing object of type long +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-12]] +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: READ of size 4 at {{.*}} with type float accesses part of an existing object of type long that starts at offset -4 +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-15]] +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type float accesses part of an existing object of type long that starts at offset -4 +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-18]] +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: READ of size 4 at {{.*}} with type float partially accesses an object of type short that starts at offset 2 +// CHECK: {{#0 0x.* in add_flt .*basic.c:}}[[@LINE-21]] +} + +int main(int argc, char *argv[]) { + int x = atoi(argv[1]); + add_flt((float *) &x); + printf("x = %d\n", x); + + long y = x; + add_flt((float *) &y); + printf("y = %ld\n", y); + + add_flt(((float *) &y) + 1); + printf("y = %ld\n", y); + + char *mem = (char *) malloc(4*sizeof(short)); + memset(mem, 0, 4*sizeof(short)); + *(short *) (mem + 2) = x; + add_flt((float*) mem); + short s1 = *(short *) mem; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: READ of size 2 at {{.*}} with type short accesses an existing object of type float +// CHECK: {{#0 0x.* in main .*basic.c:}}[[@LINE-3]] + short s2 = *(short *) (mem + 2); +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: READ of size 2 at {{.*}} with type short accesses part of an existing object of type float that starts at offset -2 +// CHECK: {{#0 0x.* in main .*basic.c:}}[[@LINE-3]] + printf("m[0] = %d, m[1] = %d\n", s1, s2); + free(mem); + + return 0; +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + Index: test/tysan/char-memcpy.c =================================================================== --- /dev/null +++ test/tysan/char-memcpy.c @@ -0,0 +1,40 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out.0 2>&1 +// RUN: FileCheck %s < %t.out.0 +// RUN: %clang_tysan -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include + +// There's no type-based-aliasing violation here: the memcpy is implemented +// using only char* or unsigned char* (both of which may alias anything). +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + +void my_memcpy_uchar(void *dest, void *src, int n) { + unsigned char *p = dest, *q = src, *end = p + n; + while (p < end) + *p++ = *q++; +} + +void my_memcpy_char(void *dest, void *src, int n) { + char *p = dest, *q = src, *end = p + n; + while (p < end) + *p++ = *q++; +} + +void test_uchar() { + struct S { short x; short *r; } s = { 10, &s.x }, s2; + my_memcpy_uchar(&s2, &s, sizeof(struct S)); + printf("%d\n", *(s2.r)); +} + +void test_char() { + struct S { short x; short *r; } s = { 10, &s.x }, s2; + my_memcpy_char(&s2, &s, sizeof(struct S)); + printf("%d\n", *(s2.r)); +} + +int main() { + test_uchar(); + test_char(); +} + Index: test/tysan/int-long.c =================================================================== --- /dev/null +++ test/tysan/int-long.c @@ -0,0 +1,23 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include + +long foo(int *x, long *y) { + *x = 0; + *y = 1; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 8 at {{.*}} with type long accesses an existing object of type int +// CHECK: {{#0 0x.* in foo .*int-long.c:}}[[@LINE-3]] + + return *x; +} + +int main(void) { + long l; + printf("%ld\n", foo((int *)&l, &l)); +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + + Index: test/tysan/lit.cfg =================================================================== --- /dev/null +++ test/tysan/lit.cfg @@ -0,0 +1,35 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'TypeSanitizer' + getattr(config, 'name_suffix', 'default') + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags used with -fsanitize=type option. +clang_tysan_cflags = (["-fsanitize=type", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls"] + + [config.target_cflags] + + config.debug_info_flags) +clang_tysan_cxxflags = config.cxx_mode_flags + clang_tysan_cflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang_tysan ", build_invocation(clang_tysan_cflags)) ) +config.substitutions.append( ("%clangxx_tysan ", build_invocation(clang_tysan_cxxflags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# TypeSanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True + +if config.target_arch != 'aarch64': + config.available_features.add('stable-runtime') + Index: test/tysan/lit.site.cfg.in =================================================================== --- /dev/null +++ test/tysan/lit.site.cfg.in @@ -0,0 +1,12 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Tool-specific config options. +config.name_suffix = "@TYSAN_TEST_CONFIG_SUFFIX@" +config.target_cflags = "@TYSAN_TEST_TARGET_CFLAGS@" +config.target_arch = "@TYSAN_TEST_TARGET_ARCH@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@TYSAN_LIT_SOURCE_DIR@/lit.cfg") Index: test/tysan/ptr-float.c =================================================================== --- /dev/null +++ test/tysan/ptr-float.c @@ -0,0 +1,21 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +float *P; +void zero_array() { + int i; + for (i = 0; i < 1; ++i) + P[i] = 0.0f; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type float accesses an existing object of type any pointer +// CHECK: {{#0 0x.* in zero_array .*ptr-float.c:}}[[@LINE-3]] + +} + +int main() { + P = (float*)&P; + zero_array(); +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + Index: test/tysan/struct-offset.c =================================================================== --- /dev/null +++ test/tysan/struct-offset.c @@ -0,0 +1,28 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include +#include + +struct X { + int i; + int j; +}; + +int foo(struct X *p, struct X *q) { + q->j = 1; + p->i = 0; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type int (in X at offset 0) accesses an existing object of type int (in X at offset 4) +// CHECK: {{#0 0x.* in foo .*struct-offset.c:}}[[@LINE-3]] + return q->j; +} + +int main() { + unsigned char *p = malloc(3 * sizeof(int)); + printf("%i\n", foo((struct X *)(p + sizeof(int)), + (struct X *)p)); +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + Index: test/tysan/struct.c =================================================================== --- /dev/null +++ test/tysan/struct.c @@ -0,0 +1,37 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include + +typedef struct S1 { int i1; } s1; +typedef struct S2 { int i2; } s2; + +void g(int *i) { + *i = 5; + printf("%i\n", *i); +} + +void h(char *c) { + *c = 5; + printf("%i\n", (int) *c); +} + +void f(s1 *s1p, s2 *s2p) { + s1p->i1 = 2; + s2p->i2 = 3; +// CHECK: ERROR: TypeSanitizer: type-aliasing-violation +// CHECK: WRITE of size 4 at {{.*}} with type int (in S2 at offset 0) accesses an existing object of type int (in S1 at offset 0) +// CHECK: {{#0 0x.* in f .*struct.c:}}[[@LINE-3]] + printf("%i\n", s1p->i1); +} + +int main() { + s1 s = {.i1 = 1}; + f(&s, (s2 *)&s); + g(&s.i1); + h((char *) &s.i1); +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + + Index: test/tysan/union-wr-wr.c =================================================================== --- /dev/null +++ test/tysan/union-wr-wr.c @@ -0,0 +1,23 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// XFAIL: * +// Clang's TBAA representation currently get's this wrong (writes to different +// members of a union type are allowed to alias (i.e. change the type)). + +#include + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + +int main() { + union { + int i; + short s; + } u; + + u.i = 42; + u.s = 1; + + printf("%dn", u.i); +} +