Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -135,6 +135,9 @@ SANITIZER_GROUP("efficiency-all", Efficiency, EfficiencyCacheFrag | EfficiencyWorkingSet) +// TypeSanitizer +SANITIZER("type", Type) + // Magic group, containing all sanitizers. For example, "-fno-sanitize=all" // can be used to disable all the sanitizers. SANITIZER_GROUP("all", All, ~0ULL) Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -69,6 +69,7 @@ bool needsEsanRt() const { return Sanitizers.hasOneOf(SanitizerKind::Efficiency); } + bool needsTysanRt() const { return Sanitizers.has(SanitizerKind::Type); } bool requiresPIE() const; bool needsUnwindTables() const; Index: lib/CodeGen/BackendUtil.cpp =================================================================== --- lib/CodeGen/BackendUtil.cpp +++ lib/CodeGen/BackendUtil.cpp @@ -284,6 +284,11 @@ PM.add(createEfficiencySanitizerPass(Opts)); } +static void addTypeSanitizerPass(const PassManagerBuilder &Builder, + legacy::PassManagerBase &PM) { + PM.add(createTypeSanitizerPass()); +} + static TargetLibraryInfoImpl *createTLII(llvm::Triple &TargetTriple, const CodeGenOptions &CodeGenOpts) { TargetLibraryInfoImpl *TLII = new TargetLibraryInfoImpl(TargetTriple); @@ -582,6 +587,13 @@ addEfficiencySanitizerPass); } + if (LangOpts.Sanitize.has(SanitizerKind::Type)) { + PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addTypeSanitizerPass); + PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + addTypeSanitizerPass); + } + // Set up the per-function pass manager. FPM.add(new TargetLibraryInfoWrapperPass(*TLII)); if (CodeGenOpts.VerifyModule) Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -434,6 +434,7 @@ CGM.setStaticLocalDeclAddress(&D, castedAddr); CGM.getSanitizerMetadata()->reportGlobalToASan(var, D); + CGM.getSanitizerMetadata()->reportGlobalToTySan(var, D); // Emit global variable debug descriptor for static vars. CGDebugInfo *DI = getDebugInfo(); Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -336,6 +336,10 @@ !isInSanitizerBlacklist(SanitizerKind::SafeStack, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SafeStack); + if (getLangOpts().Sanitize.has(SanitizerKind::Type) && + !isInSanitizerBlacklist(SanitizerKind::Type, Fn, Loc)) + Fn->addFnAttr(llvm::Attribute::SanitizeType); + return Fn; } Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -851,6 +851,8 @@ Fn->addFnAttr(llvm::Attribute::SanitizeMemory); if (SanOpts.has(SanitizerKind::SafeStack)) Fn->addFnAttr(llvm::Attribute::SafeStack); + if (SanOpts.has(SanitizerKind::Type)) + Fn->addFnAttr(llvm::Attribute::SanitizeType); // Ignore TSan memory acesses from within ObjC/ObjC++ dealloc, initialize, // .cxx_destruct, __destroy_helper_block_ and all of their calees at run time. Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -133,8 +133,9 @@ if (LangOpts.CUDA) createCUDARuntime(); - // Enable TBAA unless it's suppressed. ThreadSanitizer needs TBAA even at O0. - if (LangOpts.Sanitize.has(SanitizerKind::Thread) || + // Enable TBAA unless it's suppressed. ThreadSanitizer needs TBAA even at O0 + // (as does TypeSanitizer). + if (LangOpts.Sanitize.hasOneOf(SanitizerKind::Thread | SanitizerKind::Type) || (!CodeGenOpts.RelaxedAliasing && CodeGenOpts.OptimizationLevel > 0)) TBAA.reset(new CodeGenTBAA(Context, VMContext, CodeGenOpts, getLangOpts(), getCXXABI().getMangleContext())); @@ -2942,6 +2943,7 @@ EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); SanitizerMD->reportGlobalToASan(GV, *D, NeedsGlobalCtor); + SanitizerMD->reportGlobalToTySan(GV, *D); // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) Index: lib/CodeGen/CodeGenTBAA.cpp =================================================================== --- lib/CodeGen/CodeGenTBAA.cpp +++ lib/CodeGen/CodeGenTBAA.cpp @@ -89,8 +89,10 @@ } llvm::MDNode *CodeGenTBAA::getTypeInfo(QualType QTy) { - // At -O0 or relaxed aliasing, TBAA is not emitted for regular types. - if (CodeGenOpts.OptimizationLevel == 0 || CodeGenOpts.RelaxedAliasing) + // At -O0 or relaxed aliasing, TBAA is not emitted for regular types (unless + // we're running TypeSanitizer). + if (!Features.Sanitize.has(SanitizerKind::Type) && + (CodeGenOpts.OptimizationLevel == 0 || CodeGenOpts.RelaxedAliasing)) return nullptr; // If the type has the may_alias attribute (even on a typedef), it is Index: lib/CodeGen/SanitizerMetadata.h =================================================================== --- lib/CodeGen/SanitizerMetadata.h +++ lib/CodeGen/SanitizerMetadata.h @@ -42,6 +42,7 @@ void reportGlobalToASan(llvm::GlobalVariable *GV, SourceLocation Loc, StringRef Name, QualType Ty, bool IsDynInit = false, bool IsBlacklisted = false); + void reportGlobalToTySan(llvm::GlobalVariable *GV, const VarDecl &D); void disableSanitizerForGlobal(llvm::GlobalVariable *GV); void disableSanitizerForInstruction(llvm::Instruction *I); private: Index: lib/CodeGen/SanitizerMetadata.cpp =================================================================== --- lib/CodeGen/SanitizerMetadata.cpp +++ lib/CodeGen/SanitizerMetadata.cpp @@ -72,6 +72,31 @@ IsBlacklisted); } +void SanitizerMetadata::reportGlobalToTySan(llvm::GlobalVariable *GV, + const VarDecl &D) { + if (!CGM.getLangOpts().Sanitize.has(SanitizerKind::Type)) + return; + + for (auto Attr : D.specific_attrs()) + if (Attr->getMask() & SanitizerKind::Type) + return; + + QualType QTy = D.getType(); + llvm::MDNode *TBAAInfo = CGM.getTBAATypeInfo(QTy); + if (!TBAAInfo || TBAAInfo == CGM.getTBAATypeInfo(CGM.getContext().CharTy)) + return; + + llvm::Metadata *GlobalMetadata[] = { + llvm::ConstantAsMetadata::get(GV), TBAAInfo + }; + + llvm::MDNode *ThisGlobal = + llvm::MDNode::get(CGM.getLLVMContext(), GlobalMetadata); + llvm::NamedMDNode *TysanGlobals = + CGM.getModule().getOrInsertNamedMetadata("llvm.tysan.globals"); + TysanGlobals->addOperand(ThisGlobal); +} + void SanitizerMetadata::disableSanitizerForGlobal(llvm::GlobalVariable *GV) { // For now, just make sure the global is not modified by the ASan // instrumentation. Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -31,9 +31,10 @@ NotAllowedWithTrap = Vptr, NotAllowedWithMinimalRuntime = Vptr, RequiresPIE = DataFlow, - NeedsUnwindTables = Address | Thread | Memory | DataFlow, + NeedsUnwindTables = Address | Thread | Memory | DataFlow | Type, SupportsCoverage = Address | KernelAddress | Memory | Leak | Undefined | - Integer | Nullability | DataFlow | Fuzzer | FuzzerNoLink, + Integer | Nullability | DataFlow | Type | + Fuzzer | FuzzerNoLink, RecoverableByDefault = Undefined | Integer | Nullability, Unrecoverable = Unreachable | Return, LegacyFsanitizeRecoverMask = Undefined | Integer, @@ -106,6 +107,8 @@ BlacklistFile = "cfi_blacklist.txt"; else if (Kinds & (Undefined | Integer | Nullability)) BlacklistFile = "ubsan_blacklist.txt"; + else if (Kinds & Type) + BlacklistFile = "tysan_blacklist.txt"; if (BlacklistFile) { clang::SmallString<64> Path(D.ResourceDir); @@ -376,7 +379,10 @@ std::make_pair(Efficiency, Leak), std::make_pair(Efficiency, Thread), std::make_pair(Efficiency, Memory), - std::make_pair(Efficiency, KernelAddress)}; + std::make_pair(Efficiency, KernelAddress), + std::make_pair(Type, Address), std::make_pair(Type, KernelAddress), + std::make_pair(Type, Memory), std::make_pair(Type, Leak), + std::make_pair(Type, Thread), std::make_pair(Type, Efficiency)}; for (auto G : IncompatibleGroups) { SanitizerMask Group = G.first; if (Kinds & Group) { Index: lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- lib/Driver/ToolChains/CommonArgs.cpp +++ lib/Driver/ToolChains/CommonArgs.cpp @@ -630,6 +630,8 @@ } if (SanArgs.needsEsanRt()) StaticRuntimes.push_back("esan"); + if (SanArgs.needsTysanRt()) + StaticRuntimes.push_back("tysan"); } // Should be called before we add system libraries (C++ ABI, libstdc++/libc++, Index: lib/Driver/ToolChains/Linux.cpp =================================================================== --- lib/Driver/ToolChains/Linux.cpp +++ lib/Driver/ToolChains/Linux.cpp @@ -837,6 +837,8 @@ if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch) Res |= SanitizerKind::Leak; if (IsX86_64 || IsMIPS64 || IsAArch64 || IsPowerPC64) + Res |= SanitizerKind::Type; + if (IsX86_64 || IsMIPS64 || IsAArch64 || IsPowerPC64) Res |= SanitizerKind::Thread; if (IsX86_64 || IsMIPS64 || IsPowerPC64 || IsAArch64) Res |= SanitizerKind::Memory; Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1138,6 +1138,7 @@ .Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow)) .Case("efficiency_sanitizer", LangOpts.Sanitize.hasOneOf(SanitizerKind::Efficiency)) + .Case("type_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Type)) // Objective-C features .Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? .Case("objc_arc", LangOpts.ObjCAutoRefCount) Index: test/CodeGen/sanitize-type-attr.cpp =================================================================== --- /dev/null +++ test/CodeGen/sanitize-type-attr.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefix=WITHOUT %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -fsanitize=type | FileCheck -check-prefix=TYSAN %s +// RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -fsanitize=type -fsanitize-blacklist=%t | FileCheck -check-prefix=BL %s + +// The sanitize_type attribute should be attached to functions +// when TypeSanitizer is enabled, unless no_sanitize("type") attribute +// is present. + +// WITHOUT: NoTYSAN1{{.*}}) [[NOATTR:#[0-9]+]] +// BL: NoTYSAN1{{.*}}) [[NOATTR:#[0-9]+]] +// TYSAN: NoTYSAN1{{.*}}) [[NOATTR:#[0-9]+]] +__attribute__((no_sanitize("type"))) +int NoTYSAN1(int *a) { return *a; } + +// WITHOUT: NoTYSAN2{{.*}}) [[NOATTR]] +// BL: NoTYSAN2{{.*}}) [[NOATTR]] +// TYSAN: NoTYSAN2{{.*}}) [[NOATTR]] +__attribute__((no_sanitize("type"))) +int NoTYSAN2(int *a); +int NoTYSAN2(int *a) { return *a; } + +// WITHOUT: NoTYSAN3{{.*}}) [[NOATTR:#[0-9]+]] +// BL: NoTYSAN3{{.*}}) [[NOATTR:#[0-9]+]] +// TYSAN: NoTYSAN3{{.*}}) [[NOATTR:#[0-9]+]] +__attribute__((no_sanitize("type"))) +int NoTYSAN3(int *a) { return *a; } + +// WITHOUT: TYSANOk{{.*}}) [[NOATTR]] +// BL: TYSANOk{{.*}}) [[NOATTR]] +// TYSAN: TYSANOk{{.*}}) [[WITH:#[0-9]+]] +int TYSANOk(int *a) { return *a; } + +// WITHOUT: TemplateTYSANOk{{.*}}) [[NOATTR]] +// BL: TemplateTYSANOk{{.*}}) [[NOATTR]] +// TYSAN: TemplateTYSANOk{{.*}}) [[WITH]] +template +int TemplateTYSANOk() { return i; } + +// WITHOUT: TemplateNoTYSAN{{.*}}) [[NOATTR]] +// BL: TemplateNoTYSAN{{.*}}) [[NOATTR]] +// TYSAN: TemplateNoTYSAN{{.*}}) [[NOATTR]] +template +__attribute__((no_sanitize("type"))) +int TemplateNoTYSAN() { return i; } + +int force_instance = TemplateTYSANOk<42>() + + TemplateNoTYSAN<42>(); + +// Check that __cxx_global_var_init* get the sanitize_type attribute. +int global1 = 0; +int global2 = *(int*)((char*)&global1+1); +// WITHOUT: @__cxx_global_var_init{{.*}}[[NOATTR:#[0-9]+]] +// BL: @__cxx_global_var_init{{.*}}[[NOATTR:#[0-9]+]] +// TYSAN: @__cxx_global_var_init{{.*}}[[WITH:#[0-9]+]] + +// Make sure that we don't add globals to the list for which we don't have a +// specific type description. +struct SX { int a, b; }; +SX sx; + +// WITHOUT: attributes [[NOATTR]] = { noinline nounwind{{.*}} } + +// BL: attributes [[NOATTR]] = { noinline nounwind{{.*}} } + +// TYSAN: attributes [[NOATTR]] = { noinline nounwind{{.*}} } +// TYSAN: attributes [[WITH]] = { noinline nounwind sanitize_type{{.*}} } + +// TYSAN-DAG: !llvm.tysan.globals = !{[[G1MD:![0-9]+]], [[G2MD:![0-9]+]], [[G3MD:![0-9]+]]} +// TYSAN-DAG: [[G1MD]] = !{i32* @force_instance, [[INTMD:![0-9]+]]} +// TYSAN-DAG: [[INTMD]] = !{!"int", +// TYSAN-DAG: [[G2MD]] = !{i32* @global1, [[INTMD]]} +// TYSAN-DAG: [[G3MD]] = !{i32* @global2, [[INTMD]]} +// TYSAN-DAG: Simple C++ TBAA + Index: test/Driver/sanitizer-ld.c =================================================================== --- test/Driver/sanitizer-ld.c +++ test/Driver/sanitizer-ld.c @@ -239,6 +239,18 @@ // RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target x86_64-unknown-linux -fuse-ld=ld -stdlib=platform -lstdc++ \ +// RUN: -fsanitize=type \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-TYSAN-LINUX-CXX %s +// +// CHECK-TYSAN-LINUX-CXX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}" +// CHECK-TYSAN-LINUX-CXX-NOT: stdc++ +// CHECK-TYSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.tysan-x86_64.a" "-no-whole-archive" +// CHECK-TYSAN-LINUX-CXX: stdc++ + +// RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-unknown-linux -fuse-ld=ld -stdlib=platform -lstdc++ \ // RUN: -fsanitize=memory \ // RUN: -resource-dir=%S/Inputs/resource_dir \ // RUN: --sysroot=%S/Inputs/basic_linux_tree \