[compiler-rt] 641fbf1 - [TySan] Add initial Type Sanitizer runtime (#76261)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Dec 17 10:49:56 PST 2024
Author: Florian Hahn
Date: 2024-12-17T18:49:50Z
New Revision: 641fbf1524338c86c952ebb1ec8d2b497ada3cef
URL: https://github.com/llvm/llvm-project/commit/641fbf1524338c86c952ebb1ec8d2b497ada3cef
DIFF: https://github.com/llvm/llvm-project/commit/641fbf1524338c86c952ebb1ec8d2b497ada3cef.diff
LOG: [TySan] Add initial Type Sanitizer runtime (#76261)
This patch introduces the runtime components for type sanitizer: a
sanitizer for type-based aliasing violations.
It is based on Hal Finkel's https://reviews.llvm.org/D32197.
C/C++ have type-based aliasing rules, and LLVM's optimizer can exploit
these given TBAA metadata added by Clang. Roughly, a pointer of given
type cannot be used to access an object of a different type (with, of
course, certain exceptions). Unfortunately, there's a lot of code in the
wild that violates these rules (e.g. for type punning), and such code
often must be built with -fno-strict-aliasing. Performance is often
sacrificed as a result. Part of the problem is the difficulty of finding
TBAA violations. Hopefully, this sanitizer will help.
For each TBAA type-access descriptor, encoded in LLVM's IR using
metadata, the corresponding instrumentation pass generates descriptor
tables. Thus, for each type (and access descriptor), we have a unique
pointer representation. Excepting anonymous-namespace types, these
tables are comdat, so the pointer values should be unique across the
program. The descriptors refer to other descriptors to form a type
aliasing tree (just like LLVM's TBAA metadata does). The instrumentation
handles the "fast path" (where the types match exactly and no
partial-overlaps are detected), and defers to the runtime to handle all
of the more-complicated cases. The runtime, of course, is also
responsible for reporting errors when those are detected.
The runtime uses essentially the same shadow memory region as tsan, and
we use 8 bytes of shadow memory, the size of the pointer to the type
descriptor, for every byte of accessed data in the program. The value 0
is used to represent an unknown type. The value -1 is used to represent
an interior byte (a byte that is part of a type, but not the first
byte). The instrumentation first checks for an exact match between the
type of the current access and the type for that address recorded in the
shadow memory. If it matches, it then checks the shadow for the
remainder of the bytes in the type to make sure that they're all -1. If
not, we call the runtime. If the exact match fails, we next check if the
value is 0 (i.e. unknown). If it is, then we check the shadow for the
remainder of the byes in the type (to make sure they're all 0). If
they're not, we call the runtime. We then set the shadow for the access
address and set the shadow for the remaining bytes in the type to -1
(i.e. marking them as interior bytes). If the type indicated by the
shadow memory for the access address is neither an exact match nor 0, we
call the runtime.
The instrumentation pass inserts calls to the memset intrinsic to set
the memory updated by memset, memcpy, and memmove, as well as
allocas/byval (and for lifetime.start/end) to reset the shadow memory to
reflect that the type is now unknown. The runtime intercepts memset,
memcpy, etc. to perform the same function for the library calls.
The runtime essentially repeats these checks, but uses the full TBAA
algorithm, just as the compiler does, to determine when two types are
permitted to alias. In a situation where access overlap has occurred and
aliasing is not permitted, an error is generated.
As a note, this implementation does not use the compressed shadow-memory
scheme discussed previously
(http://lists.llvm.org/pipermail/llvm-dev/2017-April/111766.html). That
scheme would not handle the struct-path (i.e. structure offset)
information that our TBAA represents. I expect we'll want to further
work on compressing the shadow-memory representation, but I think it
makes sense to do that as follow-up work.
This includes build fixes for Linux from Mingjie Xu.
Depends on #76260 (Clang support), #76259 (LLVM support)
PR: https://github.com/llvm/llvm-project/pull/76261
Added:
compiler-rt/lib/tysan/CMakeLists.txt
compiler-rt/lib/tysan/lit.cfg
compiler-rt/lib/tysan/lit.site.cfg.in
compiler-rt/lib/tysan/tysan.cpp
compiler-rt/lib/tysan/tysan.h
compiler-rt/lib/tysan/tysan.syms.extra
compiler-rt/lib/tysan/tysan_flags.inc
compiler-rt/lib/tysan/tysan_interceptors.cpp
compiler-rt/lib/tysan/tysan_platform.h
compiler-rt/test/tysan/CMakeLists.txt
compiler-rt/test/tysan/anon-ns.cpp
compiler-rt/test/tysan/anon-same-struct.c
compiler-rt/test/tysan/anon-struct.c
compiler-rt/test/tysan/basic.c
compiler-rt/test/tysan/char-memcpy.c
compiler-rt/test/tysan/constexpr-subobject.cpp
compiler-rt/test/tysan/global.c
compiler-rt/test/tysan/int-long.c
compiler-rt/test/tysan/lit.cfg.py
compiler-rt/test/tysan/lit.site.cfg.py.in
compiler-rt/test/tysan/ptr-float.c
compiler-rt/test/tysan/struct-offset-multiple-compilation-units.cpp
compiler-rt/test/tysan/struct-offset.c
compiler-rt/test/tysan/struct.c
compiler-rt/test/tysan/union-wr-wr.c
compiler-rt/test/tysan/violation-pr45282.c
compiler-rt/test/tysan/violation-pr47137.c
compiler-rt/test/tysan/violation-pr51837.c
compiler-rt/test/tysan/violation-pr62544.c
compiler-rt/test/tysan/violation-pr62828.cpp
compiler-rt/test/tysan/violation-pr68655.cpp
compiler-rt/test/tysan/violation-pr86685.c
Modified:
clang/runtime/CMakeLists.txt
compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
compiler-rt/cmake/config-ix.cmake
Removed:
################################################################################
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 5a1e8db61023b0..ab5d55a9a35c04 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..e3ef6c9c971475
--- /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 @@
+ at 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..39d78e7c95e0cd
--- /dev/null
+++ b/compiler-rt/lib/tysan/tysan.cpp
@@ -0,0 +1,346 @@
+//===-- 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"
+
+#include <string.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 ";
+ size_t TIPrefixLen = strlen(TIPrefix);
+
+ const char *DName = Symbolizer::GetOrInit()->Demangle(Name);
+ if (!internal_strncmp(DName, TIPrefix, TIPrefixLen))
+ DName += TIPrefixLen;
+
+ return DName;
+}
+
+static void printTDName(tysan_type_descriptor *td) {
+ if (((sptr)td) <= 0) {
+ Printf("<unknown type>");
+ return;
+ }
+
+ switch (td->Tag) {
+ default:
+ CHECK(false && "invalid enum value");
+ 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 {
+ CHECK(false && "invalid enum value");
+ 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 {
+ CHECK(false && "invalid enum value");
+ 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
diff erent 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..97df28037b0d23
--- /dev/null
+++ b/compiler-rt/lib/tysan/tysan.h
@@ -0,0 +1,78 @@
+//===-- 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.
+};
+
+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
+//
diff erent in
diff erent 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..c473ffe5e445bd
--- /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 --allow-empty %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..817a3f0921e8d4
--- /dev/null
+++ b/compiler-rt/test/tysan/lit.cfg.py
@@ -0,0 +1,147 @@
+# -*- Python -*-
+
+import os
+import platform
+import re
+import shlex
+
+import lit.formats
+
+
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if attr_value is not None:
+ 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):
+ 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)
+
+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 = 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)
+
+# 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 = Tr
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 @@
+ at 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..aaa98959869886
--- /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 p1 float
+ // 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..b3d8b0a6465fda
--- /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..fb895ff729de4c
--- /dev/null
+++ b/compiler-rt/test/tysan/violation-pr47137.c
@@ -0,0 +1,41 @@
+// 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 <stdint.h>
+#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
+ // 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];
+ ((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..65dd3332721169
--- /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..709132c4aba64d
--- /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..7be05c7a7d4f80
--- /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..43b8d478e6851d
--- /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;
+}
More information about the llvm-commits
mailing list