https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/76261
>From 524eb555b0473bd93401297c5deba77f4dbd83fe Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Fri, 22 Nov 2024 15:01:41 +0000 Subject: [PATCH 1/8] [TySan] A Type Sanitizer (Runtime Library) --- clang/runtime/CMakeLists.txt | 2 +- .../cmake/Modules/AllSupportedArchDefs.cmake | 1 + compiler-rt/cmake/config-ix.cmake | 15 +- compiler-rt/lib/tysan/CMakeLists.txt | 64 ++++ compiler-rt/lib/tysan/lit.cfg | 35 ++ compiler-rt/lib/tysan/lit.site.cfg.in | 12 + compiler-rt/lib/tysan/tysan.cpp | 344 ++++++++++++++++++ compiler-rt/lib/tysan/tysan.h | 79 ++++ compiler-rt/lib/tysan/tysan.syms.extra | 2 + compiler-rt/lib/tysan/tysan_flags.inc | 17 + compiler-rt/lib/tysan/tysan_interceptors.cpp | 250 +++++++++++++ compiler-rt/lib/tysan/tysan_platform.h | 93 +++++ compiler-rt/test/tysan/CMakeLists.txt | 32 ++ compiler-rt/test/tysan/anon-ns.cpp | 41 +++ compiler-rt/test/tysan/anon-same-struct.c | 26 ++ compiler-rt/test/tysan/anon-struct.c | 27 ++ compiler-rt/test/tysan/basic.c | 65 ++++ compiler-rt/test/tysan/char-memcpy.c | 45 +++ .../test/tysan/constexpr-subobject.cpp | 25 ++ compiler-rt/test/tysan/global.c | 31 ++ compiler-rt/test/tysan/int-long.c | 21 ++ compiler-rt/test/tysan/lit.cfg.py | 139 +++++++ compiler-rt/test/tysan/lit.site.cfg.py.in | 17 + compiler-rt/test/tysan/ptr-float.c | 19 + ...ruct-offset-multiple-compilation-units.cpp | 51 +++ compiler-rt/test/tysan/struct-offset.c | 26 ++ compiler-rt/test/tysan/struct.c | 39 ++ compiler-rt/test/tysan/union-wr-wr.c | 18 + compiler-rt/test/tysan/violation-pr45282.c | 32 ++ compiler-rt/test/tysan/violation-pr47137.c | 40 ++ compiler-rt/test/tysan/violation-pr51837.c | 34 ++ compiler-rt/test/tysan/violation-pr62544.c | 24 ++ compiler-rt/test/tysan/violation-pr62828.cpp | 44 +++ compiler-rt/test/tysan/violation-pr68655.cpp | 40 ++ compiler-rt/test/tysan/violation-pr86685.c | 29 ++ 35 files changed, 1777 insertions(+), 2 deletions(-) create mode 100644 compiler-rt/lib/tysan/CMakeLists.txt create mode 100644 compiler-rt/lib/tysan/lit.cfg create mode 100644 compiler-rt/lib/tysan/lit.site.cfg.in create mode 100644 compiler-rt/lib/tysan/tysan.cpp create mode 100644 compiler-rt/lib/tysan/tysan.h create mode 100644 compiler-rt/lib/tysan/tysan.syms.extra create mode 100644 compiler-rt/lib/tysan/tysan_flags.inc create mode 100644 compiler-rt/lib/tysan/tysan_interceptors.cpp create mode 100644 compiler-rt/lib/tysan/tysan_platform.h create mode 100644 compiler-rt/test/tysan/CMakeLists.txt create mode 100644 compiler-rt/test/tysan/anon-ns.cpp create mode 100644 compiler-rt/test/tysan/anon-same-struct.c create mode 100644 compiler-rt/test/tysan/anon-struct.c create mode 100644 compiler-rt/test/tysan/basic.c create mode 100644 compiler-rt/test/tysan/char-memcpy.c create mode 100644 compiler-rt/test/tysan/constexpr-subobject.cpp create mode 100644 compiler-rt/test/tysan/global.c create mode 100644 compiler-rt/test/tysan/int-long.c create mode 100644 compiler-rt/test/tysan/lit.cfg.py create mode 100644 compiler-rt/test/tysan/lit.site.cfg.py.in create mode 100644 compiler-rt/test/tysan/ptr-float.c create mode 100644 compiler-rt/test/tysan/struct-offset-multiple-compilation-units.cpp create mode 100644 compiler-rt/test/tysan/struct-offset.c create mode 100644 compiler-rt/test/tysan/struct.c create mode 100644 compiler-rt/test/tysan/union-wr-wr.c create mode 100644 compiler-rt/test/tysan/violation-pr45282.c create mode 100644 compiler-rt/test/tysan/violation-pr47137.c create mode 100644 compiler-rt/test/tysan/violation-pr51837.c create mode 100644 compiler-rt/test/tysan/violation-pr62544.c create mode 100644 compiler-rt/test/tysan/violation-pr62828.cpp create mode 100644 compiler-rt/test/tysan/violation-pr68655.cpp create mode 100644 compiler-rt/test/tysan/violation-pr86685.c diff --git a/clang/runtime/CMakeLists.txt b/clang/runtime/CMakeLists.txt index 65fcdc2868f031..ff2605b23d25b0 100644 --- a/clang/runtime/CMakeLists.txt +++ b/clang/runtime/CMakeLists.txt @@ -122,7 +122,7 @@ if(LLVM_BUILD_EXTERNAL_COMPILER_RT AND EXISTS ${COMPILER_RT_SRC_ROOT}/) COMPONENT compiler-rt) # Add top-level targets that build specific compiler-rt runtimes. - set(COMPILER_RT_RUNTIMES fuzzer asan builtins dfsan lsan msan profile tsan ubsan ubsan-minimal) + set(COMPILER_RT_RUNTIMES fuzzer asan builtins dfsan lsan msan profile tsan tysan ubsan ubsan-minimal) foreach(runtime ${COMPILER_RT_RUNTIMES}) get_ext_project_build_command(build_runtime_cmd ${runtime}) add_custom_target(${runtime} diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake index b29ae179c2b4f4..ad6784c7ba8833 100644 --- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake +++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake @@ -85,6 +85,7 @@ else() set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64} ${S390X} ${LOONGARCH64} ${RISCV64}) endif() +set(ALL_TYSAN_SUPPORTED_ARCH ${X86_64} ${ARM64}) set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON} ${LOONGARCH64}) diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index 6d52eecc9a91fe..cf729c3adb1f5f 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -458,6 +458,7 @@ if(APPLE) set(SANITIZER_COMMON_SUPPORTED_OS osx) set(PROFILE_SUPPORTED_OS osx) set(TSAN_SUPPORTED_OS osx) + set(TYSAN_SUPPORTED_OS osx) set(XRAY_SUPPORTED_OS osx) set(FUZZER_SUPPORTED_OS osx) set(ORC_SUPPORTED_OS) @@ -593,6 +594,7 @@ if(APPLE) list(APPEND FUZZER_SUPPORTED_OS ${platform}) list(APPEND ORC_SUPPORTED_OS ${platform}) list(APPEND UBSAN_SUPPORTED_OS ${platform}) + list(APPEND TYSAN_SUPPORTED_OS ${platform}) list(APPEND LSAN_SUPPORTED_OS ${platform}) list(APPEND STATS_SUPPORTED_OS ${platform}) endif() @@ -651,6 +653,9 @@ if(APPLE) list_intersect(CTX_PROFILE_SUPPORTED_ARCH ALL_CTX_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) @@ -703,6 +708,7 @@ else() filter_available_targets(PROFILE_SUPPORTED_ARCH ${ALL_PROFILE_SUPPORTED_ARCH}) filter_available_targets(CTX_PROFILE_SUPPORTED_ARCH ${ALL_CTX_PROFILE_SUPPORTED_ARCH}) filter_available_targets(TSAN_SUPPORTED_ARCH ${ALL_TSAN_SUPPORTED_ARCH}) + filter_available_targets(TYSAN_SUPPORTED_ARCH ${ALL_TYSAN_SUPPORTED_ARCH}) filter_available_targets(UBSAN_SUPPORTED_ARCH ${ALL_UBSAN_SUPPORTED_ARCH}) filter_available_targets(SAFESTACK_SUPPORTED_ARCH ${ALL_SAFESTACK_SUPPORTED_ARCH}) @@ -748,7 +754,7 @@ if(COMPILER_RT_SUPPORTED_ARCH) endif() message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}") -set(ALL_SANITIZERS asan;rtsan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo_standalone;ubsan_minimal;gwp_asan;nsan;asan_abi) +set(ALL_SANITIZERS asan;rtsan;dfsan;msan;hwasan;tsan;tysan;safestack;cfi;scudo_standalone;ubsan_minimal;gwp_asan;nsan;asan_abi) set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})") list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}") @@ -843,6 +849,13 @@ else() set(COMPILER_RT_HAS_CTX_PROFILE FALSE) endif() +if (COMPILER_RT_HAS_SANITIZER_COMMON AND TYSAN_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux|Darwin") + set(COMPILER_RT_HAS_TYSAN TRUE) +else() + set(COMPILER_RT_HAS_TYSAN FALSE) +endif() + if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH) if (OS_NAME MATCHES "Linux|Darwin|FreeBSD|NetBSD") set(COMPILER_RT_HAS_TSAN TRUE) diff --git a/compiler-rt/lib/tysan/CMakeLists.txt b/compiler-rt/lib/tysan/CMakeLists.txt new file mode 100644 index 00000000000000..859b67928f004a --- /dev/null +++ b/compiler-rt/lib/tysan/CMakeLists.txt @@ -0,0 +1,64 @@ +include_directories(..) + +# Runtime library sources and build flags. +set(TYSAN_SOURCES + tysan.cpp + tysan_interceptors.cpp) +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) + +add_compiler_rt_object_libraries(RTTysan_dynamic + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${TYSAN_SUPPORTED_ARCH} + SOURCES ${TYSAN_SOURCES} + ADDITIONAL_HEADERS ${TYSAN_HEADERS} + CFLAGS ${TYSAN_DYNAMIC_CFLAGS} + DEFS ${TYSAN_DYNAMIC_DEFINITIONS}) + + +# Static runtime library. +add_compiler_rt_component(tysan) + + +if(APPLE) + add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS) + + add_compiler_rt_runtime(clang_rt.tysan + SHARED + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${TYSAN_SUPPORTED_ARCH} + OBJECT_LIBS RTTysan_dynamic + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonSymbolizer + CFLAGS ${TYSAN_DYNAMIC_CFLAGS} + LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS} + DEFS ${TYSAN_DYNAMIC_DEFINITIONS} + PARENT_TARGET tysan) + + add_compiler_rt_runtime(clang_rt.tysan_static + STATIC + ARCHS ${TYSAN_SUPPORTED_ARCH} + OBJECT_LIBS RTTysan_static + CFLAGS ${TYSAN_CFLAGS} + DEFS ${TYSAN_COMMON_DEFINITIONS} + PARENT_TARGET tysan) +else() + 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_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}> + CFLAGS ${TYSAN_CFLAGS} + PARENT_TARGET tysan) + endforeach() +endif() diff --git a/compiler-rt/lib/tysan/lit.cfg b/compiler-rt/lib/tysan/lit.cfg new file mode 100644 index 00000000000000..bd2bbe855529a7 --- /dev/null +++ b/compiler-rt/lib/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') + diff --git a/compiler-rt/lib/tysan/lit.site.cfg.in b/compiler-rt/lib/tysan/lit.site.cfg.in new file mode 100644 index 00000000000000..673d04e514379b --- /dev/null +++ b/compiler-rt/lib/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") diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp new file mode 100644 index 00000000000000..f1b6bdcf0d8261 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -0,0 +1,344 @@ +//===-- tysan.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 a part of TypeSanitizer. +// +// TypeSanitizer runtime. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_flags.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(const void *addr, uptr size) { + if (tysan_inited) + internal_memset(shadow_for(addr), 0, size * sizeof(uptr)); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +tysan_copy_types(const void *daddr, const void *saddr, uptr size) { + if (tysan_inited) + internal_memmove(shadow_for(daddr), shadow_for(saddr), size * sizeof(uptr)); +} + +static const char *getDisplayName(const char *Name) { + if (Name[0] == '\0') + return "<anonymous type>"; + + // 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 (((sptr)td) <= 0) { + Printf("<unknown type>"); + 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 %zu)", 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, int TDAOffset) { + // 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 - TDAOffset; + TDA = TDA->Member.Base; + } + + do { + if (TDA == TDB) { + return OffsetA == OffsetB; + } + + if (TDA->Tag == TYSAN_STRUCT_TD) { + // Reached root type descriptor. + if (!TDA->Struct.MemberCount) + 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, int TDAOffset = 0) { + 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; + + // TDB may have been adjusted by offset TDAOffset in the caller to point to + // the outer type. Check for aliasing with and without adjusting for this + // offset. + return isAliasingLegalUp(TDA, TDB, 0) || isAliasingLegalUp(TDB, TDA, 0) || + isAliasingLegalUp(TDA, TDB, TDAOffset); +} + +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(); } +}; +} // namespace __tysan + +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 %llu)\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) { + + bool request_fast = StackTrace::WillUseFastUnwind(true); + BufferedStackTrace ST; + ST.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, request_fast); + 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 (((sptr)OldTD) < 0) { + int i = -((sptr)OldTD); + OldTDPtr -= i; + OldTD = *OldTDPtr; + + if (!isAliasingLegal(td, OldTD, i)) + 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 (((sptr)OldTD) >= 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 TySanInitializePlatformEarly() { + AvoidCVE_2016_2143(); +#ifdef TYSAN_RUNTIME_VMA + vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); +#if defined(__aarch64__) && !SANITIZER_APPLE + 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(); + } +#endif +#endif + + __sanitizer::InitializePlatformEarly(); + + __tysan_shadow_memory_address = ShadowAddr(); + __tysan_app_memory_mask = AppMask(); +} + +namespace __tysan { +bool tysan_inited = false; +bool tysan_init_is_running; +} // namespace __tysan + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() { + CHECK(!tysan_init_is_running); + if (tysan_inited) + return; + tysan_init_is_running = true; + + InitializeFlags(); + TySanInitializePlatformEarly(); + + InitializeInterceptors(); + + if (!MmapFixedNoReserve(ShadowAddr(), AppAddr() - ShadowAddr())) + Die(); + + 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 diff --git a/compiler-rt/lib/tysan/tysan.h b/compiler-rt/lib/tysan/tysan.h new file mode 100644 index 00000000000000..ec6f9587e9ce58 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan.h @@ -0,0 +1,79 @@ +//===-- tysan.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 TypeSanitizer. +// +// Private TySan header. +//===----------------------------------------------------------------------===// + +#ifndef TYSAN_H +#define TYSAN_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +using __sanitizer::sptr; +using __sanitizer::u16; +using __sanitizer::uptr; + +#include "tysan_platform.h" + +extern "C" { +void tysan_set_type_unknown(const void *addr, uptr size); +void tysan_copy_types(const void *daddr, const 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(const 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 diff --git a/compiler-rt/lib/tysan/tysan.syms.extra b/compiler-rt/lib/tysan/tysan.syms.extra new file mode 100644 index 00000000000000..04e78543161998 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan.syms.extra @@ -0,0 +1,2 @@ +tysan_* +__tysan_* diff --git a/compiler-rt/lib/tysan/tysan_flags.inc b/compiler-rt/lib/tysan/tysan_flags.inc new file mode 100644 index 00000000000000..98b6591f844ef0 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan_flags.inc @@ -0,0 +1,17 @@ +//===-- tysan_flags.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 +// +//===----------------------------------------------------------------------===// +// +// 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. diff --git a/compiler-rt/lib/tysan/tysan_interceptors.cpp b/compiler-rt/lib/tysan/tysan_interceptors.cpp new file mode 100644 index 00000000000000..5fc6f244122727 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan_interceptors.cpp @@ -0,0 +1,250 @@ +//===-- tysan_interceptors.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 a part of TypeSanitizer. +// +// Interceptors for standard library functions. +//===----------------------------------------------------------------------===// + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" +#include "tysan/tysan.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_copy_types(dst, src, 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_copy_types(dst, src, 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; +} + +#if !SANITIZER_APPLE +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; +} +#endif + +INTERCEPTOR(char *, strdup, const char *s) { + char *res = REAL(strdup)(s); + if (res) + tysan_copy_types(res, const_cast<char *>(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<char *>(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; +} + +#if SANITIZER_INTERCEPT_MEMALIGN +INTERCEPTOR(void *, memalign, uptr alignment, uptr size) { + void *res = REAL(memalign)(alignment, size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} +#define TYSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign) +#else +#define TYSAN_MAYBE_INTERCEPT_MEMALIGN +#endif // SANITIZER_INTERCEPT_MEMALIGN + +#if SANITIZER_INTERCEPT___LIBC_MEMALIGN +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; +} +#define TYSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN \ + INTERCEPT_FUNCTION(__libc_memalign) +#else +#define TYSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN +#endif // SANITIZER_INTERCEPT___LIBC_MEMALIGN + +#if SANITIZER_INTERCEPT_PVALLOC +INTERCEPTOR(void *, pvalloc, uptr size) { + void *res = REAL(pvalloc)(size); + if (res) + tysan_set_type_unknown(res, size); + return res; +} +#define TYSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc) +#else +#define TYSAN_MAYBE_INTERCEPT_PVALLOC +#endif // SANITIZER_INTERCEPT_PVALLOC + +#if SANITIZER_INTERCEPT_ALIGNED_ALLOC +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; +} +#define TYSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc) +#else +#define TYSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC +#endif + +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); + TYSAN_MAYBE_INTERCEPT_MEMALIGN; + TYSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN; + TYSAN_MAYBE_INTERCEPT_PVALLOC; + TYSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC + INTERCEPT_FUNCTION(posix_memalign); + + INTERCEPT_FUNCTION(memset); + INTERCEPT_FUNCTION(memmove); + INTERCEPT_FUNCTION(memcpy); + + inited = 1; +} +} // namespace __tysan diff --git a/compiler-rt/lib/tysan/tysan_platform.h b/compiler-rt/lib/tysan/tysan_platform.h new file mode 100644 index 00000000000000..f01392885d9398 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan_platform.h @@ -0,0 +1,93 @@ +//===------------------------ tysan_platform.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 TypeSanitizer. +// +// Platform specific information for TySan. +//===----------------------------------------------------------------------===// + +#ifndef TYSAN_PLATFORM_H +#define TYSAN_PLATFORM_H + +namespace __tysan { + +#if defined(__x86_64__) || SANITIZER_APPLE +struct Mapping { + static const uptr kShadowAddr = 0x010000000000ull; + static const uptr kAppAddr = 0x550000000000ull; + static const uptr kAppMemMsk = ~0x780000000000ull; +}; +#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 +#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 <typename Mapping, int Type> 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 <int Type> uptr MappingArchImpl(void) { +#if defined(__aarch64__) && !SANITIZER_APPLE + switch (vmaSize) { + case 39: + return MappingImpl<Mapping39, Type>(); + case 42: + return MappingImpl<Mapping42, Type>(); + case 48: + return MappingImpl<Mapping48, Type>(); + } + DCHECK(0); + return 0; +#else + return MappingImpl<Mapping, Type>(); +#endif +} + +ALWAYS_INLINE +uptr ShadowAddr() { return MappingArchImpl<MAPPING_SHADOW_ADDR>(); } + +ALWAYS_INLINE +uptr AppAddr() { return MappingArchImpl<MAPPING_APP_ADDR>(); } + +ALWAYS_INLINE +uptr AppMask() { return MappingArchImpl<MAPPING_APP_MASK>(); } + +} // namespace __tysan + +#endif diff --git a/compiler-rt/test/tysan/CMakeLists.txt b/compiler-rt/test/tysan/CMakeLists.txt new file mode 100644 index 00000000000000..76f57501e854e6 --- /dev/null +++ b/compiler-rt/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.py.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py) + 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") diff --git a/compiler-rt/test/tysan/anon-ns.cpp b/compiler-rt/test/tysan/anon-ns.cpp new file mode 100644 index 00000000000000..681304411df315 --- /dev/null +++ b/compiler-rt/test/tysan/anon-ns.cpp @@ -0,0 +1,41 @@ +// 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: %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <iostream> + +// 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; +}; +} // namespace + +#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 diff --git a/compiler-rt/test/tysan/anon-same-struct.c b/compiler-rt/test/tysan/anon-same-struct.c new file mode 100644 index 00000000000000..b9044f2a0a73c8 --- /dev/null +++ b/compiler-rt/test/tysan/anon-same-struct.c @@ -0,0 +1,26 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> + +// 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); +} diff --git a/compiler-rt/test/tysan/anon-struct.c b/compiler-rt/test/tysan/anon-struct.c new file mode 100644 index 00000000000000..25f6633545928c --- /dev/null +++ b/compiler-rt/test/tysan/anon-struct.c @@ -0,0 +1,27 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> + +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 <anonymous type> at offset 0) accesses an existing object of type int (in <anonymous type> 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 diff --git a/compiler-rt/test/tysan/basic.c b/compiler-rt/test/tysan/basic.c new file mode 100644 index 00000000000000..8e66e1a7213838 --- /dev/null +++ b/compiler-rt/test/tysan/basic.c @@ -0,0 +1,65 @@ +// 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +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 diff --git a/compiler-rt/test/tysan/char-memcpy.c b/compiler-rt/test/tysan/char-memcpy.c new file mode 100644 index 00000000000000..ebbb6b53d0f374 --- /dev/null +++ b/compiler-rt/test/tysan/char-memcpy.c @@ -0,0 +1,45 @@ +// 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 <stdio.h> + +// 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(); +} diff --git a/compiler-rt/test/tysan/constexpr-subobject.cpp b/compiler-rt/test/tysan/constexpr-subobject.cpp new file mode 100644 index 00000000000000..9cae310554c9b4 --- /dev/null +++ b/compiler-rt/test/tysan/constexpr-subobject.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// CHECK-NOT: TypeSanitizer + +int foo() { return 0; } + +struct Bar { + struct S2 { + int (*fnA)(); + int (*fnB)(); + }; + + static int x() { return 0; } + + static const S2 &get() { + static constexpr S2 Info = {&foo, &Bar::x}; + return Info; + } +}; + +int main() { + auto Info = Bar::get(); + return Info.fnB(); +} diff --git a/compiler-rt/test/tysan/global.c b/compiler-rt/test/tysan/global.c new file mode 100644 index 00000000000000..247ee768a81626 --- /dev/null +++ b/compiler-rt/test/tysan/global.c @@ -0,0 +1,31 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +#include <stdlib.h> +#include <string.h> + +float P; +long L; + +int main() { + *(int *)&P = 5; + // CHECK: ERROR: TypeSanitizer: type-aliasing-violation + // CHECK: WRITE of size 4 at {{.*}} with type int accesses an existing object of type float + // CHECK: {{#0 0x.* in main .*global.c:}}[[@LINE-3]] + + void *mem = malloc(sizeof(long)); + *(int *)mem = 6; + memcpy(mem, &L, sizeof(L)); + *(int *)mem = 8; + // CHECK: ERROR: TypeSanitizer: type-aliasing-violation + // CHECK: WRITE of size 4 at {{.*}} with type int accesses an existing object of type long + // CHECK: {{#0 0x.* in main .*global.c:}}[[@LINE-3]] + int r = *(((int *)mem) + 1); + // CHECK: ERROR: TypeSanitizer: type-aliasing-violation + // CHECK: READ of size 4 at {{.*}} with type int accesses part of an existing object of type long that starts at offset -4 + // CHECK: {{#0 0x.* in main .*global.c:}}[[@LINE-3]] + free(mem); + + return r; +} + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation diff --git a/compiler-rt/test/tysan/int-long.c b/compiler-rt/test/tysan/int-long.c new file mode 100644 index 00000000000000..b7956c07376e8e --- /dev/null +++ b/compiler-rt/test/tysan/int-long.c @@ -0,0 +1,21 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> + +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 diff --git a/compiler-rt/test/tysan/lit.cfg.py b/compiler-rt/test/tysan/lit.cfg.py new file mode 100644 index 00000000000000..05c8f0664d5e65 --- /dev/null +++ b/compiler-rt/test/tysan/lit.cfg.py @@ -0,0 +1,139 @@ +# -*- Python -*- + +import os +import platform +import re + +import lit.formats + +# Get shlex.quote if available (added in 3.3), and fall back to pipes.quote if +# it's not available. +try: + import shlex + sh_quote = shlex.quote +except: + import pipes + sh_quote = pipes.quote + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg.py " % attr_name) + return attr_value + +def push_dynamic_library_lookup_path(config, new_path): + if platform.system() == 'Windows': + dynamic_library_lookup_var = 'PATH' + elif platform.system() == 'Darwin': + dynamic_library_lookup_var = 'DYLD_LIBRARY_PATH' + else: + dynamic_library_lookup_var = 'LD_LIBRARY_PATH' + + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, ''))) + config.environment[dynamic_library_lookup_var] = new_ld_library_path + + if platform.system() == 'FreeBSD': + dynamic_library_lookup_var = 'LD_32_LIBRARY_PATH' + new_ld_32_library_path = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, ''))) + config.environment[dynamic_library_lookup_var] = new_ld_32_library_path + + if platform.system() == 'SunOS': + dynamic_library_lookup_var = 'LD_LIBRARY_PATH_32' + new_ld_library_path_32 = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, ''))) + config.environment[dynamic_library_lookup_var] = new_ld_library_path_32 + + dynamic_library_lookup_var = 'LD_LIBRARY_PATH_64' + new_ld_library_path_64 = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, ''))) + config.environment[dynamic_library_lookup_var] = new_ld_library_path_64 + +# Setup config name. +config.name = 'TypeSanitizer' + config.name_suffix + +# Platform-specific default TYSAN_OPTIONS for lit tests. +default_tysan_opts = list(config.default_sanitizer_opts) + +# On Darwin, leak checking is not enabled by default. Enable on macOS +# tests to prevent regressions +if config.host_os == 'Darwin' and config.apple_platform == 'osx': + default_tysan_opts += ['detect_leaks=1'] + +default_tysan_opts_str = ':'.join(default_tysan_opts) +if default_tysan_opts_str: + config.environment['TYSAN_OPTIONS'] = default_tysan_opts_str + default_tysan_opts_str += ':' +config.substitutions.append(('%env_tysan_opts=', + 'env TYSAN_OPTIONS=' + default_tysan_opts_str)) + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +if config.host_os not in ['FreeBSD', 'NetBSD']: + libdl_flag = "-ldl" +else: + libdl_flag = "" + +# GCC-ASan doesn't link in all the necessary libraries automatically, so +# we have to do it ourselves. +if config.compiler_id == 'GNU': + extra_link_flags = ["-pthread", "-lstdc++", libdl_flag] +else: + extra_link_flags = [] + +# Setup default compiler flags used with -fsanitize=address option. +# FIXME: Review the set of required flags and check if it can be reduced. +target_cflags = [get_required_attr(config, "target_cflags")] + extra_link_flags +target_cxxflags = config.cxx_mode_flags + target_cflags +clang_tysan_static_cflags = (["-fsanitize=type", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls"] + + config.debug_info_flags + target_cflags) +if config.target_arch == 's390x': + clang_tysan_static_cflags.append("-mbackchain") +clang_tysan_static_cxxflags = config.cxx_mode_flags + clang_tysan_static_cflags + +clang_tysan_cflags = clang_tysan_static_cflags +clang_tysan_cxxflags = clang_tysan_static_cxxflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang ", build_invocation(target_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) ) +config.substitutions.append( ("%clang_tysan ", build_invocation(clang_tysan_cflags)) ) +config.substitutions.append( ("%clangxx_tysan ", build_invocation(clang_tysan_cxxflags)) ) + + +# FIXME: De-hardcode this path. +tysan_source_dir = os.path.join( + get_required_attr(config, "compiler_rt_src_root"), "lib", "tysan") +python_exec = sh_quote(get_required_attr(config, "python_executable")) + +# Set LD_LIBRARY_PATH to pick dynamic runtime up properly. +push_dynamic_library_lookup_path(config, config.compiler_rt_libdir) + +# Default test suffixes. +config.suffixes = ['.c', '.cpp'] + +if config.host_os == 'Darwin': + config.suffixes.append('.mm') + +if config.host_os == 'Windows': + config.substitutions.append(('%fPIC', '')) + config.substitutions.append(('%fPIE', '')) + config.substitutions.append(('%pie', '')) +else: + config.substitutions.append(('%fPIC', '-fPIC')) + config.substitutions.append(('%fPIE', '-fPIE')) + config.substitutions.append(('%pie', '-pie')) + +# Only run the tests on supported OSs. +if config.host_os not in ['Linux', 'Darwin',]: + config.unsupported = True diff --git a/compiler-rt/test/tysan/lit.site.cfg.py.in b/compiler-rt/test/tysan/lit.site.cfg.py.in new file mode 100644 index 00000000000000..b56dce4fed7a26 --- /dev/null +++ b/compiler-rt/test/tysan/lit.site.cfg.py.in @@ -0,0 +1,17 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Tool-specific config options. +config.name_suffix = "@TYSAN_TEST_CONFIG_SUFFIX@" +config.target_cflags = "@TYSAN_TEST_TARGET_CFLAGS@" +config.clang = "@TYSAN_TEST_TARGET_CC@" +config.bits = "@TYSAN_TEST_BITS@" +config.arm_thumb = "@COMPILER_RT_ARM_THUMB@" +config.apple_platform = "@TYSAN_TEST_APPLE_PLATFORM@" +config.apple_platform_min_deployment_target_flag = "@TYSAN_TEST_MIN_DEPLOYMENT_TARGET_FLAG@" +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.py") diff --git a/compiler-rt/test/tysan/ptr-float.c b/compiler-rt/test/tysan/ptr-float.c new file mode 100644 index 00000000000000..61fa5f1afd70ac --- /dev/null +++ b/compiler-rt/test/tysan/ptr-float.c @@ -0,0 +1,19 @@ +// 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 diff --git a/compiler-rt/test/tysan/struct-offset-multiple-compilation-units.cpp b/compiler-rt/test/tysan/struct-offset-multiple-compilation-units.cpp new file mode 100644 index 00000000000000..f7baa14d15affa --- /dev/null +++ b/compiler-rt/test/tysan/struct-offset-multiple-compilation-units.cpp @@ -0,0 +1,51 @@ +// RUN: %clangxx_tysan -O0 %s -c -o %t.o +// RUN: %clangxx_tysan -O0 %s -DPMAIN -c -o %tm.o +// RUN: %clangxx_tysan -O0 %s -DPINIT -c -o %tinit.o +// RUN: %clangxx_tysan -O0 %t.o %tm.o %tinit.o -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +extern "C" { +typedef struct X { + int *start; + int *end; + int i; +} X; +}; + +#ifdef PMAIN +int foo(struct X *); +void bar(struct X *); +void init(struct X *); + +int main() { + struct X x; + init(&x); + printf("%d\n", foo(&x)); + free(x.start); + return 0; +} + +#elif PINIT + +void init(struct X *x) { + x->start = (int *)calloc(100, sizeof(int)); + x->end = x->start + 99; + x->i = 0; +} + +#else + +__attribute__((noinline)) int foo(struct X *x) { + if (x->start < x->end) + return 30; + return 10; +} + +void bar(struct X *x) { x->end = NULL; } + +#endif + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation diff --git a/compiler-rt/test/tysan/struct-offset.c b/compiler-rt/test/tysan/struct-offset.c new file mode 100644 index 00000000000000..7295e0ae121ed7 --- /dev/null +++ b/compiler-rt/test/tysan/struct-offset.c @@ -0,0 +1,26 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> +#include <stdlib.h> + +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 diff --git a/compiler-rt/test/tysan/struct.c b/compiler-rt/test/tysan/struct.c new file mode 100644 index 00000000000000..f7ecef59676244 --- /dev/null +++ b/compiler-rt/test/tysan/struct.c @@ -0,0 +1,39 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> + +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 diff --git a/compiler-rt/test/tysan/union-wr-wr.c b/compiler-rt/test/tysan/union-wr-wr.c new file mode 100644 index 00000000000000..6414bbfcf9d95b --- /dev/null +++ b/compiler-rt/test/tysan/union-wr-wr.c @@ -0,0 +1,18 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> + +// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation + +int main() { + union { + int i; + short s; + } u; + + u.i = 42; + u.s = 1; + + printf("%d\n", u.i); +} diff --git a/compiler-rt/test/tysan/violation-pr45282.c b/compiler-rt/test/tysan/violation-pr45282.c new file mode 100644 index 00000000000000..f3583d6be6f6a3 --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr45282.c @@ -0,0 +1,32 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// https://github.com/llvm/llvm-project/issues/45282 + +#include <stdio.h> + +int main(void) { + + double a[29], b[20]; + int i, j; + + for (i = 0; i < 20; ++i) { + b[i] = 2.01f + 1.f; + ((float *)a)[i] = 2.01f * 2.0145f; + ((float *)a + 38)[i] = 2.01f * 1.0123f; + } + + // CHECK: TypeSanitizer: type-aliasing-violation on address + // CHECK-NEXT: WRITE of size 8 at {{.+}} with type double accesses an existing object of type float + // CHECK-NEXT: in main violation-pr45282.c:25 + + // loop of problems + for (j = 2; j <= 4; ++j) { + a[j - 1] = ((float *)a)[j] * ((float *)a + 38)[j - 1]; + ((float *)a + 38)[j - 1] = ((float *)a)[j - 1] + b[j - 1]; + } + + printf("((float *)a + 38)[2] = %f\n", ((float *)a + 38)[2]); + + return 0; +} diff --git a/compiler-rt/test/tysan/violation-pr47137.c b/compiler-rt/test/tysan/violation-pr47137.c new file mode 100644 index 00000000000000..3987128ff6fc67 --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr47137.c @@ -0,0 +1,40 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// https://github.com/llvm/llvm-project/issues/47137 +#include <stdio.h> +#include <stdlib.h> + +void f(int m) { + int n = (4 * m + 2) / 3; + uint64_t *a = malloc(n * sizeof(uint64_t)); + uint64_t *b = malloc(n * sizeof(uint64_t)); + uint64_t aa[] = {0xffff3e0000000001, 0x22eaf0b680a88c16, 0x5a65d25ac40e20f3, + 0x34e7ac346236953e, 0x9dea3e0a26c6ba89, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000}; + uint64_t bb[] = {0x0000000024c0ffff, 0x000000004634d940, 0x00000000219d18ef, + 0x0000000000154519, 0x000000000000035f, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000}; + char l[20]; + l[0] = 0; + for (int i = 0; i < n; i++) { + a[i] = aa[i] + l[0] - '0'; + b[i] = bb[i] + l[0] - '0'; + } + + // CHECK: TypeSanitizer: type-aliasing-violation on address + // CHECK-NEXT: READ of size 2 at {{.+}} with type short accesses an existing object of type long long + // CHECK-NEXT: in f violation-pr47137.c:30 + for (int i = 0, j = 0; j < 4 * m; i += 4, j += 3) { + for (int k = 0; k < 3; k++) { + ((uint16_t *)a)[j + k] = ((uint16_t *)a)[i + k]; + ((uint16_t *)b)[j + k] = ((uint16_t *)b)[i + k]; + } + } + + printf("a: %016llx\n", a[0]); + free(a); + free(b); +} + +int main() { f(6); } diff --git a/compiler-rt/test/tysan/violation-pr51837.c b/compiler-rt/test/tysan/violation-pr51837.c new file mode 100644 index 00000000000000..d49a813933d653 --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr51837.c @@ -0,0 +1,34 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdint.h> +#include <stdio.h> + +// CHECK-NOT: TypeSanitizer + +union a { + int16_t b; + uint64_t c; +} d; + +uint64_t *e = &d.c; +static uint16_t f(int16_t a, int32_t b, uint64_t c); +static int64_t g(int32_t aa, uint8_t h, union a bb) { + int16_t *i = &d.b; + f(0, h, 0); + *i = h; + return 0; +} +uint16_t f(int16_t a, int32_t b, uint64_t c) { + for (d.c = 0; 0;) + ; + *e = 0; + return 0; +} + +int main() { + uint32_t j = 8; + g(1, j, d); + printf("%d\n", d.b); + return 0; +} diff --git a/compiler-rt/test/tysan/violation-pr62544.c b/compiler-rt/test/tysan/violation-pr62544.c new file mode 100644 index 00000000000000..30610925ba385f --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr62544.c @@ -0,0 +1,24 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// https://github.com/llvm/llvm-project/issues/62544 + +int printf(const char *, ...); +int a, b, c; +long d; +int main() { + short *e = &a; + int *f = &a; + *f = 0; + for (; b <= 9; b++) { + int **g = &f; + *f = d; + *g = &c; + } + + // CHECK: TypeSanitizer: type-aliasing-violation on address + // CHECK-NEXT: WRITE of size 2 at {{.+}} with type short accesses an existing object of type int + // CHECK-NEXT: in main violation-pr62544.c:22 + *e = 3; + printf("%d\n", a); +} diff --git a/compiler-rt/test/tysan/violation-pr62828.cpp b/compiler-rt/test/tysan/violation-pr62828.cpp new file mode 100644 index 00000000000000..33003df9761f52 --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr62828.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// https://github.com/llvm/llvm-project/issues/62828 +#include <stdio.h> + +typedef int int_v8[8]; +typedef short short_v8[8]; +short *test1(int_v8 *cast_c_array, short_v8 *shuf_c_array1, int *ptr) { + int *input1 = reinterpret_cast<int *>(((int_v8 *)(cast_c_array))); + short *input2 = reinterpret_cast<short *>(reinterpret_cast<int_v8 *>(input1)); + + short *output1 = reinterpret_cast<short *>(((short_v8 *)(shuf_c_array1))); + short *output2 = + reinterpret_cast<short *>(reinterpret_cast<short_v8 *>(output1)); + + for (int r = 0; r < 8; ++r) { + int tmp = (int)((r * 4) + ptr[r]); + if ((ptr[r] / 4) == 0) { + int *input = reinterpret_cast<int *>(((int_v8 *)(cast_c_array))); + input[r] = tmp; + } + } + + // CHECK: ERROR: TypeSanitizer: type-aliasing-violation on address + // CHECK-NEXT: READ of size 2 at {{.+}} with type short accesses an existing object of type int + // CHECK-NEXT: in test1(int (*) [8], short (*) [8], int*) violation-pr62828.cpp:29 + for (int i3 = 0; i3 < 4; ++i3) { + output2[i3] = input2[(i3 * 2)]; + } + return output2; +} + +int main() { + int_v8 in[4] = {{4, 4, 4, 4}}; + short_v8 out[4] = {{0}}; + int ptr[8] = {2}; + test1(in, out, ptr); + short *p = reinterpret_cast<short *>(out); + for (int i = 0; i < 32; i++) { + printf("%d ", p[i]); + } + return 0; +} diff --git a/compiler-rt/test/tysan/violation-pr68655.cpp b/compiler-rt/test/tysan/violation-pr68655.cpp new file mode 100644 index 00000000000000..ac20f8c94e1ffd --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr68655.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// https://github.com/llvm/llvm-project/issues/68655 +struct S1 { + long long a; + long long b; +}; + +// CHECK: TypeSanitizer: type-aliasing-violation on address +// CHECK-NEXT: READ of size 4 at {{.+}} with type int accesses an existing object of type long long (in S1 at offset 0) +// CHECK-NEXT: in copyMem(S1*, S1*) violation-pr68655.cpp:19 + +void inline copyMem(S1 *dst, S1 *src) { + unsigned *d = reinterpret_cast<unsigned *>(dst); + unsigned *s = reinterpret_cast<unsigned *>(src); + + for (int i = 0; i < sizeof(S1) / sizeof(unsigned); i++) { + *d = *s; + d++; + s++; + } +} + +void math(S1 *dst, int *srcA, int idx_t) { + S1 zero[4]; + for (int i = 0; i < 2; i++) { + zero[i].a = i + idx_t; + zero[i].b = i * idx_t; + } + + copyMem(&dst[idx_t], &zero[srcA[idx_t]]); +} + +int main() { + S1 dst = {0}; + int Src[2] = {0, 0}; + math(&dst, &Src[0], 0); + return 0; +} diff --git a/compiler-rt/test/tysan/violation-pr86685.c b/compiler-rt/test/tysan/violation-pr86685.c new file mode 100644 index 00000000000000..fe4fd82af5fdd2 --- /dev/null +++ b/compiler-rt/test/tysan/violation-pr86685.c @@ -0,0 +1,29 @@ +// RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> +#include <stdlib.h> + +// Violation reported in https://github.com/llvm/llvm-project/issues/86685. +void foo(int *s, float *f, long n) { + for (long i = 0; i < n; ++i) { + *f = 2; + if (i == 1) + break; + + // CHECK: TypeSanitizer: type-aliasing-violation on address + // CHECK-NEXT: WRITE of size 4 at {{.+}} with type int accesses an existing object of type float + // CHECK-NEXT: #0 {{.+}} in foo violation-pr86685.c:17 + *s = 4; + } +} + +int main(void) { + union { + int s; + float f; + } u = {0}; + foo(&u.s, &u.f, 2); + printf("%.f\n", u.f); + return 0; +} >From 6f48a28a75b65d46b8362a5703e27698fc09b598 Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Fri, 6 Dec 2024 12:08:52 +0000 Subject: [PATCH 2/8] !fixup update tests --- compiler-rt/test/tysan/constexpr-subobject.cpp | 2 +- compiler-rt/test/tysan/ptr-float.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler-rt/test/tysan/constexpr-subobject.cpp b/compiler-rt/test/tysan/constexpr-subobject.cpp index 9cae310554c9b4..c473ffe5e445bd 100644 --- a/compiler-rt/test/tysan/constexpr-subobject.cpp +++ b/compiler-rt/test/tysan/constexpr-subobject.cpp @@ -1,5 +1,5 @@ // RUN: %clang_tysan -O0 %s -o %t && %run %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out +// RUN: FileCheck --allow-empty %s < %t.out // CHECK-NOT: TypeSanitizer diff --git a/compiler-rt/test/tysan/ptr-float.c b/compiler-rt/test/tysan/ptr-float.c index 61fa5f1afd70ac..aaa98959869886 100644 --- a/compiler-rt/test/tysan/ptr-float.c +++ b/compiler-rt/test/tysan/ptr-float.c @@ -7,7 +7,7 @@ void zero_array() { 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: WRITE of size 4 at {{.*}} with type float accesses an existing object of type p1 float // CHECK: {{#0 0x.* in zero_array .*ptr-float.c:}}[[@LINE-3]] } >From f92088f81942a7fb60525eeaa0f9cea6da08e26c Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Thu, 12 Dec 2024 09:55:13 +0000 Subject: [PATCH 3/8] !fixup remove commented out code, thanks! --- compiler-rt/lib/tysan/tysan.h | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler-rt/lib/tysan/tysan.h b/compiler-rt/lib/tysan/tysan.h index ec6f9587e9ce58..97df28037b0d23 100644 --- a/compiler-rt/lib/tysan/tysan.h +++ b/compiler-rt/lib/tysan/tysan.h @@ -47,7 +47,6 @@ struct tysan_struct_type_descriptor { struct tysan_type_descriptor *Type; uptr Offset; } Members[1]; // Tail allocated. - // char Name[]; // Tail allocated. }; struct tysan_type_descriptor { >From 758c99abb36139fe759b51bd70f9ffeac76f21fa Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Thu, 12 Dec 2024 15:43:25 +0000 Subject: [PATCH 4/8] !fixup update checks for linux. --- compiler-rt/test/tysan/violation-pr45282.c | 2 +- compiler-rt/test/tysan/violation-pr47137.c | 5 +++-- compiler-rt/test/tysan/violation-pr62544.c | 2 +- compiler-rt/test/tysan/violation-pr62828.cpp | 2 +- compiler-rt/test/tysan/violation-pr68655.cpp | 4 ++-- compiler-rt/test/tysan/violation-pr86685.c | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler-rt/test/tysan/violation-pr45282.c b/compiler-rt/test/tysan/violation-pr45282.c index f3583d6be6f6a3..b3d8b0a6465fda 100644 --- a/compiler-rt/test/tysan/violation-pr45282.c +++ b/compiler-rt/test/tysan/violation-pr45282.c @@ -18,7 +18,7 @@ int main(void) { // CHECK: TypeSanitizer: type-aliasing-violation on address // CHECK-NEXT: WRITE of size 8 at {{.+}} with type double accesses an existing object of type float - // CHECK-NEXT: in main violation-pr45282.c:25 + // CHECK-NEXT: in main {{.*/?}}violation-pr45282.c:25 // loop of problems for (j = 2; j <= 4; ++j) { diff --git a/compiler-rt/test/tysan/violation-pr47137.c b/compiler-rt/test/tysan/violation-pr47137.c index 3987128ff6fc67..11c16cb7358661 100644 --- a/compiler-rt/test/tysan/violation-pr47137.c +++ b/compiler-rt/test/tysan/violation-pr47137.c @@ -4,6 +4,7 @@ // https://github.com/llvm/llvm-project/issues/47137 #include <stdio.h> #include <stdlib.h> +#include <stdint.h> void f(int m) { int n = (4 * m + 2) / 3; @@ -23,8 +24,8 @@ void f(int m) { } // CHECK: TypeSanitizer: type-aliasing-violation on address - // CHECK-NEXT: READ of size 2 at {{.+}} with type short accesses an existing object of type long long - // CHECK-NEXT: in f violation-pr47137.c:30 + // CHECK-NEXT: READ of size 2 at {{.+}} with type short accesses an existing object of type long + // CHECK-NEXT: in f {{.*/?}}violation-pr47137.c:31 for (int i = 0, j = 0; j < 4 * m; i += 4, j += 3) { for (int k = 0; k < 3; k++) { ((uint16_t *)a)[j + k] = ((uint16_t *)a)[i + k]; diff --git a/compiler-rt/test/tysan/violation-pr62544.c b/compiler-rt/test/tysan/violation-pr62544.c index 30610925ba385f..65dd3332721169 100644 --- a/compiler-rt/test/tysan/violation-pr62544.c +++ b/compiler-rt/test/tysan/violation-pr62544.c @@ -18,7 +18,7 @@ int main() { // CHECK: TypeSanitizer: type-aliasing-violation on address // CHECK-NEXT: WRITE of size 2 at {{.+}} with type short accesses an existing object of type int - // CHECK-NEXT: in main violation-pr62544.c:22 + // CHECK-NEXT: in main {{.*/?}}violation-pr62544.c:22 *e = 3; printf("%d\n", a); } diff --git a/compiler-rt/test/tysan/violation-pr62828.cpp b/compiler-rt/test/tysan/violation-pr62828.cpp index 33003df9761f52..709132c4aba64d 100644 --- a/compiler-rt/test/tysan/violation-pr62828.cpp +++ b/compiler-rt/test/tysan/violation-pr62828.cpp @@ -24,7 +24,7 @@ short *test1(int_v8 *cast_c_array, short_v8 *shuf_c_array1, int *ptr) { // CHECK: ERROR: TypeSanitizer: type-aliasing-violation on address // CHECK-NEXT: READ of size 2 at {{.+}} with type short accesses an existing object of type int - // CHECK-NEXT: in test1(int (*) [8], short (*) [8], int*) violation-pr62828.cpp:29 + // CHECK-NEXT: in test1(int (*) [8], short (*) [8], int*) {{.*/?}}violation-pr62828.cpp:29 for (int i3 = 0; i3 < 4; ++i3) { output2[i3] = input2[(i3 * 2)]; } diff --git a/compiler-rt/test/tysan/violation-pr68655.cpp b/compiler-rt/test/tysan/violation-pr68655.cpp index ac20f8c94e1ffd..7be05c7a7d4f80 100644 --- a/compiler-rt/test/tysan/violation-pr68655.cpp +++ b/compiler-rt/test/tysan/violation-pr68655.cpp @@ -8,8 +8,8 @@ struct S1 { }; // CHECK: TypeSanitizer: type-aliasing-violation on address -// CHECK-NEXT: READ of size 4 at {{.+}} with type int accesses an existing object of type long long (in S1 at offset 0) -// CHECK-NEXT: in copyMem(S1*, S1*) violation-pr68655.cpp:19 +// CHECK-NEXT: READ of size 4 at {{.+}} with type int accesses an existing object of type long long (in {{.*}}S1 at offset 0) +// CHECK-NEXT: in copyMem(S1*, S1*) {{.*/?}}violation-pr68655.cpp:19 void inline copyMem(S1 *dst, S1 *src) { unsigned *d = reinterpret_cast<unsigned *>(dst); diff --git a/compiler-rt/test/tysan/violation-pr86685.c b/compiler-rt/test/tysan/violation-pr86685.c index fe4fd82af5fdd2..43b8d478e6851d 100644 --- a/compiler-rt/test/tysan/violation-pr86685.c +++ b/compiler-rt/test/tysan/violation-pr86685.c @@ -13,7 +13,7 @@ void foo(int *s, float *f, long n) { // CHECK: TypeSanitizer: type-aliasing-violation on address // CHECK-NEXT: WRITE of size 4 at {{.+}} with type int accesses an existing object of type float - // CHECK-NEXT: #0 {{.+}} in foo violation-pr86685.c:17 + // CHECK-NEXT: #0 {{.+}} in foo {{.*/?}}violation-pr86685.c:17 *s = 4; } } >From 43b62b9a12c47b653c95b3a16e4f627c520bd35f Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Thu, 12 Dec 2024 15:57:12 +0000 Subject: [PATCH 5/8] !fixup fix formatting in test --- compiler-rt/test/tysan/violation-pr47137.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-rt/test/tysan/violation-pr47137.c b/compiler-rt/test/tysan/violation-pr47137.c index 11c16cb7358661..fb895ff729de4c 100644 --- a/compiler-rt/test/tysan/violation-pr47137.c +++ b/compiler-rt/test/tysan/violation-pr47137.c @@ -2,9 +2,9 @@ // RUN: FileCheck %s < %t.out // https://github.com/llvm/llvm-project/issues/47137 +#include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <stdint.h> void f(int m) { int n = (4 * m + 2) / 3; >From c3aa78ab1ec7ae809a17161d3cd27ec0ba7daab4 Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Thu, 12 Dec 2024 22:31:22 +0000 Subject: [PATCH 6/8] !fixup fix python formatting --- compiler-rt/test/tysan/lit.cfg.py | 178 +++++++++++++++++------------- 1 file changed, 100 insertions(+), 78 deletions(-) diff --git a/compiler-rt/test/tysan/lit.cfg.py b/compiler-rt/test/tysan/lit.cfg.py index 05c8f0664d5e65..49b8665a9b871f 100644 --- a/compiler-rt/test/tysan/lit.cfg.py +++ b/compiler-rt/test/tysan/lit.cfg.py @@ -9,131 +9,153 @@ # Get shlex.quote if available (added in 3.3), and fall back to pipes.quote if # it's not available. try: - import shlex - sh_quote = shlex.quote + import shlex + + sh_quote = shlex.quote except: - import pipes - sh_quote = pipes.quote + import pipes + + sh_quote = pipes.quote + def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if attr_value == None: - lit_config.fatal( - "No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg.py " % attr_name) - return attr_value + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg.py " % attr_name + ) + return attr_value + def push_dynamic_library_lookup_path(config, new_path): - if platform.system() == 'Windows': - dynamic_library_lookup_var = 'PATH' - elif platform.system() == 'Darwin': - dynamic_library_lookup_var = 'DYLD_LIBRARY_PATH' - else: - dynamic_library_lookup_var = 'LD_LIBRARY_PATH' - - new_ld_library_path = os.path.pathsep.join( - (new_path, config.environment.get(dynamic_library_lookup_var, ''))) - config.environment[dynamic_library_lookup_var] = new_ld_library_path - - if platform.system() == 'FreeBSD': - dynamic_library_lookup_var = 'LD_32_LIBRARY_PATH' - new_ld_32_library_path = os.path.pathsep.join( - (new_path, config.environment.get(dynamic_library_lookup_var, ''))) - config.environment[dynamic_library_lookup_var] = new_ld_32_library_path - - if platform.system() == 'SunOS': - dynamic_library_lookup_var = 'LD_LIBRARY_PATH_32' - new_ld_library_path_32 = os.path.pathsep.join( - (new_path, config.environment.get(dynamic_library_lookup_var, ''))) - config.environment[dynamic_library_lookup_var] = new_ld_library_path_32 - - dynamic_library_lookup_var = 'LD_LIBRARY_PATH_64' - new_ld_library_path_64 = os.path.pathsep.join( - (new_path, config.environment.get(dynamic_library_lookup_var, ''))) - config.environment[dynamic_library_lookup_var] = new_ld_library_path_64 + if platform.system() == "Windows": + dynamic_library_lookup_var = "PATH" + elif platform.system() == "Darwin": + dynamic_library_lookup_var = "DYLD_LIBRARY_PATH" + else: + dynamic_library_lookup_var = "LD_LIBRARY_PATH" + + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, "")) + ) + config.environment[dynamic_library_lookup_var] = new_ld_library_path + + if platform.system() == "FreeBSD": + dynamic_library_lookup_var = "LD_32_LIBRARY_PATH" + new_ld_32_library_path = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, "")) + ) + config.environment[dynamic_library_lookup_var] = new_ld_32_library_path + + if platform.system() == "SunOS": + dynamic_library_lookup_var = "LD_LIBRARY_PATH_32" + new_ld_library_path_32 = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, "")) + ) + config.environment[dynamic_library_lookup_var] = new_ld_library_path_32 + + dynamic_library_lookup_var = "LD_LIBRARY_PATH_64" + new_ld_library_path_64 = os.path.pathsep.join( + (new_path, config.environment.get(dynamic_library_lookup_var, "")) + ) + config.environment[dynamic_library_lookup_var] = new_ld_library_path_64 + # Setup config name. -config.name = 'TypeSanitizer' + config.name_suffix +config.name = "TypeSanitizer" + config.name_suffix # Platform-specific default TYSAN_OPTIONS for lit tests. default_tysan_opts = list(config.default_sanitizer_opts) # On Darwin, leak checking is not enabled by default. Enable on macOS # tests to prevent regressions -if config.host_os == 'Darwin' and config.apple_platform == 'osx': - default_tysan_opts += ['detect_leaks=1'] +if config.host_os == "Darwin" and config.apple_platform == "osx": + default_tysan_opts += ["detect_leaks=1"] -default_tysan_opts_str = ':'.join(default_tysan_opts) +default_tysan_opts_str = ":".join(default_tysan_opts) if default_tysan_opts_str: - config.environment['TYSAN_OPTIONS'] = default_tysan_opts_str - default_tysan_opts_str += ':' -config.substitutions.append(('%env_tysan_opts=', - 'env TYSAN_OPTIONS=' + default_tysan_opts_str)) + config.environment["TYSAN_OPTIONS"] = default_tysan_opts_str + default_tysan_opts_str += ":" +config.substitutions.append( + ("%env_tysan_opts=", "env TYSAN_OPTIONS=" + default_tysan_opts_str) +) # Setup source root. config.test_source_root = os.path.dirname(__file__) -if config.host_os not in ['FreeBSD', 'NetBSD']: - libdl_flag = "-ldl" +if config.host_os not in ["FreeBSD", "NetBSD"]: + libdl_flag = "-ldl" else: - libdl_flag = "" + libdl_flag = "" # GCC-ASan doesn't link in all the necessary libraries automatically, so # we have to do it ourselves. -if config.compiler_id == 'GNU': - extra_link_flags = ["-pthread", "-lstdc++", libdl_flag] +if config.compiler_id == "GNU": + extra_link_flags = ["-pthread", "-lstdc++", libdl_flag] else: - extra_link_flags = [] + extra_link_flags = [] # Setup default compiler flags used with -fsanitize=address option. # FIXME: Review the set of required flags and check if it can be reduced. target_cflags = [get_required_attr(config, "target_cflags")] + extra_link_flags target_cxxflags = config.cxx_mode_flags + target_cflags -clang_tysan_static_cflags = (["-fsanitize=type", - "-mno-omit-leaf-frame-pointer", - "-fno-omit-frame-pointer", - "-fno-optimize-sibling-calls"] + - config.debug_info_flags + target_cflags) -if config.target_arch == 's390x': - clang_tysan_static_cflags.append("-mbackchain") +clang_tysan_static_cflags = ( + [ + "-fsanitize=type", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls", + ] + + config.debug_info_flags + + target_cflags +) +if config.target_arch == "s390x": + clang_tysan_static_cflags.append("-mbackchain") clang_tysan_static_cxxflags = config.cxx_mode_flags + clang_tysan_static_cflags -clang_tysan_cflags = clang_tysan_static_cflags +clang_tysan_cflags = clang_tysan_static_cflags clang_tysan_cxxflags = clang_tysan_static_cxxflags + def build_invocation(compile_flags): - return " " + " ".join([config.clang] + compile_flags) + " " + return " " + " ".join([config.clang] + compile_flags) + " " + -config.substitutions.append( ("%clang ", build_invocation(target_cflags)) ) -config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) ) -config.substitutions.append( ("%clang_tysan ", build_invocation(clang_tysan_cflags)) ) -config.substitutions.append( ("%clangxx_tysan ", build_invocation(clang_tysan_cxxflags)) ) +config.substitutions.append(("%clang ", build_invocation(target_cflags))) +config.substitutions.append(("%clangxx ", build_invocation(target_cxxflags))) +config.substitutions.append(("%clang_tysan ", build_invocation(clang_tysan_cflags))) +config.substitutions.append(("%clangxx_tysan ", build_invocation(clang_tysan_cxxflags))) # FIXME: De-hardcode this path. tysan_source_dir = os.path.join( - get_required_attr(config, "compiler_rt_src_root"), "lib", "tysan") + get_required_attr(config, "compiler_rt_src_root"), "lib", "tysan" +) python_exec = sh_quote(get_required_attr(config, "python_executable")) # Set LD_LIBRARY_PATH to pick dynamic runtime up properly. push_dynamic_library_lookup_path(config, config.compiler_rt_libdir) # Default test suffixes. -config.suffixes = ['.c', '.cpp'] +config.suffixes = [".c", ".cpp"] -if config.host_os == 'Darwin': - config.suffixes.append('.mm') +if config.host_os == "Darwin": + config.suffixes.append(".mm") -if config.host_os == 'Windows': - config.substitutions.append(('%fPIC', '')) - config.substitutions.append(('%fPIE', '')) - config.substitutions.append(('%pie', '')) +if config.host_os == "Windows": + config.substitutions.append(("%fPIC", "")) + config.substitutions.append(("%fPIE", "")) + config.substitutions.append(("%pie", "")) else: - config.substitutions.append(('%fPIC', '-fPIC')) - config.substitutions.append(('%fPIE', '-fPIE')) - config.substitutions.append(('%pie', '-pie')) + config.substitutions.append(("%fPIC", "-fPIC")) + config.substitutions.append(("%fPIE", "-fPIE")) + config.substitutions.append(("%pie", "-pie")) # Only run the tests on supported OSs. -if config.host_os not in ['Linux', 'Darwin',]: - config.unsupported = True +if config.host_os not in [ + "Linux", + "Darwin", +]: + config.unsupported = Tr >From 17c457771023570f3b274ff61c6d426d2a801e68 Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Mon, 16 Dec 2024 11:34:27 +0000 Subject: [PATCH 7/8] !fixup address latest comments, thanks! --- compiler-rt/lib/tysan/lit.cfg | 2 +- compiler-rt/lib/tysan/tysan.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler-rt/lib/tysan/lit.cfg b/compiler-rt/lib/tysan/lit.cfg index bd2bbe855529a7..e3ef6c9c971475 100644 --- a/compiler-rt/lib/tysan/lit.cfg +++ b/compiler-rt/lib/tysan/lit.cfg @@ -13,7 +13,7 @@ clang_tysan_cflags = (["-fsanitize=type", "-mno-omit-leaf-frame-pointer", "-fno-omit-frame-pointer", "-fno-optimize-sibling-calls"] + - [config.target_cflags] + + config.target_cflags + config.debug_info_flags) clang_tysan_cxxflags = config.cxx_mode_flags + clang_tysan_cflags diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index f1b6bdcf0d8261..9a8ba2e1da3909 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -22,6 +22,8 @@ #include "tysan/tysan.h" +#include <string.h> + using namespace __sanitizer; using namespace __tysan; @@ -43,11 +45,12 @@ static const char *getDisplayName(const char *Name) { // 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 *TIPrefix = "typeinfo name for "; + size_t TIPrefixLen = strlen(TIPrefix); const char *DName = Symbolizer::GetOrInit()->Demangle(Name); - if (!internal_strncmp(DName, TIPrefix, sizeof(TIPrefix) - 1)) - DName += sizeof(TIPrefix) - 1; + if (!internal_strncmp(DName, TIPrefix, TIPrefixLen)) + DName += TIPrefixLen; return DName; } @@ -91,7 +94,7 @@ static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) { } else if (TD->Tag == TYSAN_MEMBER_TD) { TD = TD->Member.Access; } else { - DCHECK(0); + CHECK(false && "invalid enum value"); break; } } while (TD); >From 975d1edc92471de60bfaca3d16665d8bc9046154 Mon Sep 17 00:00:00 2001 From: Florian Hahn <f...@fhahn.com> Date: Tue, 17 Dec 2024 11:50:43 +0000 Subject: [PATCH 8/8] !fixup address latest comments, thanks! --- compiler-rt/lib/tysan/tysan.cpp | 7 +++---- compiler-rt/test/tysan/lit.cfg.py | 34 +++++++++---------------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index 9a8ba2e1da3909..39d78e7c95e0cd 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -63,7 +63,7 @@ static void printTDName(tysan_type_descriptor *td) { switch (td->Tag) { default: - DCHECK(0); + CHECK(false && "invalid enum value"); break; case TYSAN_MEMBER_TD: printTDName(td->Member.Access); @@ -117,9 +117,8 @@ static bool isAliasingLegalUp(tysan_type_descriptor *TDA, } do { - if (TDA == TDB) { + if (TDA == TDB) return OffsetA == OffsetB; - } if (TDA->Tag == TYSAN_STRUCT_TD) { // Reached root type descriptor. @@ -135,7 +134,7 @@ static bool isAliasingLegalUp(tysan_type_descriptor *TDA, OffsetA -= TDA->Struct.Members[Idx].Offset; TDA = TDA->Struct.Members[Idx].Type; } else { - DCHECK(0); + CHECK(false && "invalid enum value"); break; } } while (TDA); diff --git a/compiler-rt/test/tysan/lit.cfg.py b/compiler-rt/test/tysan/lit.cfg.py index 49b8665a9b871f..b53a3104fa05a3 100644 --- a/compiler-rt/test/tysan/lit.cfg.py +++ b/compiler-rt/test/tysan/lit.cfg.py @@ -3,30 +3,21 @@ import os import platform import re +import shlex import lit.formats -# Get shlex.quote if available (added in 3.3), and fall back to pipes.quote if -# it's not available. -try: - import shlex - - sh_quote = shlex.quote -except: - import pipes - - sh_quote = pipes.quote - def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) - if attr_value == None: - lit_config.fatal( - "No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg.py " % attr_name - ) - return attr_value + if attr_value : + return attr_value + + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg.py " % attr_name + ) def push_dynamic_library_lookup_path(config, new_path): @@ -69,11 +60,6 @@ def push_dynamic_library_lookup_path(config, new_path): # Platform-specific default TYSAN_OPTIONS for lit tests. default_tysan_opts = list(config.default_sanitizer_opts) -# On Darwin, leak checking is not enabled by default. Enable on macOS -# tests to prevent regressions -if config.host_os == "Darwin" and config.apple_platform == "osx": - default_tysan_opts += ["detect_leaks=1"] - default_tysan_opts_str = ":".join(default_tysan_opts) if default_tysan_opts_str: config.environment["TYSAN_OPTIONS"] = default_tysan_opts_str @@ -133,7 +119,7 @@ def build_invocation(compile_flags): tysan_source_dir = os.path.join( get_required_attr(config, "compiler_rt_src_root"), "lib", "tysan" ) -python_exec = sh_quote(get_required_attr(config, "python_executable")) +python_exec = shlex.quote(get_required_attr(config, "python_executable")) # Set LD_LIBRARY_PATH to pick dynamic runtime up properly. push_dynamic_library_lookup_path(config, config.compiler_rt_libdir) _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits