diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -473,7 +473,7 @@ LocalDeclMap.find(&D)->second = Address(castedAddr, elemTy, alignment); CGM.setStaticLocalDeclAddress(&D, castedAddr); - CGM.getSanitizerMetadata()->reportGlobalToASan(var, D); + CGM.getSanitizerMetadata()->reportGlobal(var, D); // Emit global variable debug descriptor for static vars. CGDebugInfo *DI = getDebugInfo(); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2763,18 +2763,10 @@ bool CodeGenModule::isInNoSanitizeList(llvm::GlobalVariable *GV, SourceLocation Loc, QualType Ty, StringRef Category) const { - // For now globals can be ignored only in ASan and KASan. - const SanitizerMask EnabledAsanMask = - LangOpts.Sanitize.Mask & - (SanitizerKind::Address | SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress | - SanitizerKind::MemTag); - if (!EnabledAsanMask) - return false; const auto &NoSanitizeL = getContext().getNoSanitizeList(); - if (NoSanitizeL.containsGlobal(EnabledAsanMask, GV->getName(), Category)) + if (NoSanitizeL.containsGlobal(LangOpts.Sanitize.Mask, GV->getName(), Category)) return true; - if (NoSanitizeL.containsLocation(EnabledAsanMask, Loc, Category)) + if (NoSanitizeL.containsLocation(LangOpts.Sanitize.Mask, Loc, Category)) return true; // Check global type. if (!Ty.isNull()) { @@ -2786,7 +2778,7 @@ // Only record types (classes, structs etc.) are ignored. if (Ty->isRecordType()) { std::string TypeStr = Ty.getAsString(getContext().getPrintingPolicy()); - if (NoSanitizeL.containsType(EnabledAsanMask, TypeStr, Category)) + if (NoSanitizeL.containsType(LangOpts.Sanitize.Mask, TypeStr, Category)) return true; } } @@ -4792,7 +4784,7 @@ if (NeedsGlobalCtor || NeedsGlobalDtor) EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); - SanitizerMD->reportGlobalToASan(GV, *D, NeedsGlobalCtor); + SanitizerMD->reportGlobal(GV, *D, NeedsGlobalCtor); // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) @@ -5678,7 +5670,7 @@ if (Entry) *Entry = GV; - SanitizerMD->reportGlobalToASan(GV, S->getStrTokenLoc(0), "", + SanitizerMD->reportGlobal(GV, S->getStrTokenLoc(0), "", QualType()); return ConstantAddress(castStringLiteralToDefaultAddressSpace(*this, GV), diff --git a/clang/lib/CodeGen/SanitizerMetadata.h b/clang/lib/CodeGen/SanitizerMetadata.h --- a/clang/lib/CodeGen/SanitizerMetadata.h +++ b/clang/lib/CodeGen/SanitizerMetadata.h @@ -14,13 +14,15 @@ #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/IR/GlobalVariable.h" + namespace llvm { -class GlobalVariable; class Instruction; class MDNode; -} +} // namespace llvm namespace clang { class VarDecl; @@ -34,19 +36,24 @@ void operator=(const SanitizerMetadata &) = delete; CodeGenModule &CGM; + public: SanitizerMetadata(CodeGenModule &CGM); - void reportGlobalToASan(llvm::GlobalVariable *GV, const VarDecl &D, - bool IsDynInit = false); - void reportGlobalToASan(llvm::GlobalVariable *GV, SourceLocation Loc, - StringRef Name, QualType Ty, bool IsDynInit = false, - bool IsExcluded = false); + void reportGlobal(llvm::GlobalVariable *GV, const VarDecl &D, + bool IsDynInit = false); + void reportGlobal(llvm::GlobalVariable *GV, SourceLocation Loc, + StringRef Name, QualType Ty, bool IsDynInit = false, + SanitizerMask NoSanitizeMask = {}); + void setASanSpecificMetadata(llvm::GlobalVariable::SanitizerMetadata &Meta, + llvm::GlobalVariable *GV, SourceLocation Loc, + QualType Ty, bool IsDynInit = false); void disableSanitizerForGlobal(llvm::GlobalVariable *GV); void disableSanitizerForInstruction(llvm::Instruction *I); + private: llvm::MDNode *getLocationMetadata(SourceLocation Loc); }; -} // end namespace CodeGen -} // end namespace clang +} // end namespace CodeGen +} // end namespace clang #endif diff --git a/clang/lib/CodeGen/SanitizerMetadata.cpp b/clang/lib/CodeGen/SanitizerMetadata.cpp --- a/clang/lib/CodeGen/SanitizerMetadata.cpp +++ b/clang/lib/CodeGen/SanitizerMetadata.cpp @@ -17,29 +17,90 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/Constants.h" +using GlobalVariable = llvm::GlobalVariable; +using GVSanitizerMetadata = GlobalVariable::SanitizerMetadata; +using GlobalSanitizer = GVSanitizerMetadata::GlobalSanitizer; + using namespace clang; using namespace CodeGen; SanitizerMetadata::SanitizerMetadata(CodeGenModule &CGM) : CGM(CGM) {} -static bool isAsanHwasanOrMemTag(const SanitizerSet& SS) { +static bool isAsanHwasanOrMemTag(const SanitizerSet &SS) { return SS.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress | SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress | SanitizerKind::MemTag); } -void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, - SourceLocation Loc, StringRef Name, - QualType Ty, bool IsDynInit, - bool IsExcluded) { - if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) - return; +void SanitizerMetadata::setASanSpecificMetadata(GVSanitizerMetadata &Meta, + GlobalVariable *GV, + SourceLocation Loc, QualType Ty, + bool IsDynInit) { IsDynInit &= !CGM.isInNoSanitizeList(GV, Loc, Ty, "init"); - IsExcluded |= CGM.isInNoSanitizeList(GV, Loc, Ty); + Meta.IsDynInit = IsDynInit; +} + +void SanitizerMetadata::reportGlobal(GlobalVariable *GV, SourceLocation Loc, + StringRef Name, QualType Ty, + bool IsDynInit, + SanitizerMask NoSanitizeMask) { + + GVSanitizerMetadata Meta; + if (GV->hasSanitizerMetadata()) + Meta = GV->getSanitizerMetadata(); + + bool IsExcluded = false; + if (CGM.isInNoSanitizeList(GV, Loc, Ty) || + NoSanitizeMask == SanitizerKind::All) { + Meta.AddSanitizer(GlobalSanitizer::NoSanitize); + IsExcluded = true; + } + + auto &SanTarget = CGM.getLangOpts().Sanitize; + + if (!isAsanHwasanOrMemTag(SanTarget)) + return; + + SanitizerSet NoSanitizeSet; + NoSanitizeSet.Mask = NoSanitizeMask; + + llvm::LLVMContext &VMContext = CGM.getLLVMContext(); + + if (SanTarget.hasOneOf(SanitizerKind::Address | + SanitizerKind::KernelAddress)) { + if (NoSanitizeSet.hasOneOf(SanitizerKind::Address | + SanitizerKind::KernelAddress) || + Meta.HasSanitizer(GlobalSanitizer::NoAddress)) { + Meta.AddSanitizer(GlobalSanitizer::NoAddress); + IsExcluded = true; + } else if (!IsExcluded) { + Meta.AddSanitizer(GlobalSanitizer::Address); + } + setASanSpecificMetadata(Meta, GV, Loc, Ty, IsDynInit); + } + if (SanTarget.hasOneOf(SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress)) { + if (NoSanitizeSet.hasOneOf(SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress) || + Meta.HasSanitizer(GlobalSanitizer::NoHWAddress)) { + Meta.AddSanitizer(GlobalSanitizer::NoHWAddress); + IsExcluded = true; + } else if (!IsExcluded) { + Meta.AddSanitizer(GlobalSanitizer::HWAddress); + } + } + if (SanTarget.hasOneOf(SanitizerKind::MemTag)) { + if (NoSanitizeSet.hasOneOf(SanitizerKind::MemTag) || + Meta.HasSanitizer(GlobalSanitizer::NoMemtag)) { + Meta.AddSanitizer(GlobalSanitizer::NoMemtag); + IsExcluded = true; + } else if (!IsExcluded) { + Meta.AddSanitizer(GlobalSanitizer::Memtag); + } + } llvm::Metadata *LocDescr = nullptr; llvm::Metadata *GlobalName = nullptr; - llvm::LLVMContext &VMContext = CGM.getLLVMContext(); if (!IsExcluded) { // Don't generate source location and global name if it is on // the NoSanitizeList - it won't be instrumented anyway. @@ -48,10 +109,12 @@ GlobalName = llvm::MDString::get(VMContext, Name); } + GV->setSanitizerMetadata(Meta); + llvm::Metadata *GlobalMetadata[] = { llvm::ConstantAsMetadata::get(GV), LocDescr, GlobalName, - llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::Type::getInt1Ty(VMContext), IsDynInit)), + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( + llvm::Type::getInt1Ty(VMContext), Meta.IsDynInit)), llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( llvm::Type::getInt1Ty(VMContext), IsExcluded))}; @@ -61,29 +124,27 @@ AsanGlobals->addOperand(ThisGlobal); } -void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, - const VarDecl &D, bool IsDynInit) { - if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) - return; +void SanitizerMetadata::reportGlobal(GlobalVariable *GV, const VarDecl &D, + bool IsDynInit) { + SanitizerMask NoSanitizeMask; + for (auto *Attr : D.specific_attrs()) { + NoSanitizeMask |= Attr->getMask(); + } + + if (D.hasAttr()) { + NoSanitizeMask = SanitizerKind::All; + } + std::string QualName; llvm::raw_string_ostream OS(QualName); D.printQualifiedName(OS); - bool IsExcluded = false; - for (auto Attr : D.specific_attrs()) - if (Attr->getMask() & SanitizerKind::Address) - IsExcluded = true; - if (D.hasAttr()) - IsExcluded = true; - reportGlobalToASan(GV, D.getLocation(), OS.str(), D.getType(), IsDynInit, - IsExcluded); + reportGlobal(GV, D.getLocation(), OS.str(), D.getType(), IsDynInit, + NoSanitizeMask); } -void SanitizerMetadata::disableSanitizerForGlobal(llvm::GlobalVariable *GV) { - // For now, just make sure the global is not modified by the ASan - // instrumentation. - if (isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) - reportGlobalToASan(GV, SourceLocation(), "", QualType(), false, true); +void SanitizerMetadata::disableSanitizerForGlobal(GlobalVariable *GV) { + reportGlobal(GV, SourceLocation(), "", QualType(), false, SanitizerKind::All); } void SanitizerMetadata::disableSanitizerForInstruction(llvm::Instruction *I) { diff --git a/clang/test/CodeGen/asan-globals-alias.cpp b/clang/test/CodeGen/asan-globals-alias.cpp --- a/clang/test/CodeGen/asan-globals-alias.cpp +++ b/clang/test/CodeGen/asan-globals-alias.cpp @@ -22,12 +22,12 @@ struct input_device_id joydev_ids[] = { { {1}, 1234 } }; // KASAN ignored extern struct input_device_id __attribute__((alias("joydev_ids"))) __mod_joydev_ids_device_table; -// ASAN: @aliased_global{{.*}} global { i32, [28 x i8] }{{.*}}, align 32 -// ASAN: @aliased_global_2{{.*}} global { i32, [28 x i8] }{{.*}}, align 32 -// ASAN: @joydev_ids{{.*}} global { {{.*}}[56 x i8] zeroinitializer }, align 32 +// ASAN: @aliased_global{{.*}} global { i32, [28 x i8] }{{.*}}, sanitize_address, align 32 +// ASAN: @aliased_global_2{{.*}} global { i32, [28 x i8] }{{.*}}, sanitize_address, align 32 +// ASAN: @joydev_ids{{.*}} global { {{.*}}[56 x i8] zeroinitializer }, sanitize_address, align 32 // KASAN: @aliased_global{{.*}} global i32 // KASAN: @aliased_global_2{{.*}} global i32 -// KASAN: @joydev_ids{{.*}} global [1 x {{.*}}i64 1234 }], align 16 +// KASAN: @joydev_ids{{.*}} global [1 x {{.*}}i64 1234 }], sanitize_address, align 16 // Check the aliases exist: // CHECK: @__global_alias ={{.*}} alias diff --git a/clang/test/CodeGen/asan-globals-odr.cpp b/clang/test/CodeGen/asan-globals-odr.cpp --- a/clang/test/CodeGen/asan-globals-odr.cpp +++ b/clang/test/CodeGen/asan-globals-odr.cpp @@ -14,7 +14,7 @@ return global; } -// CHECK: [[VAR:@.*global.*]] ={{.*}} global { i32, [28 x i8] } zeroinitializer, align 32 +// CHECK: [[VAR:@.*global.*]] ={{.*}} global { i32, [28 x i8] } zeroinitializer, sanitize_address, align 32 // INDICATOR0-NOT: __odr_asan_gen // INDICATOR1: [[ODR:@.*__odr_asan_gen_.*global.*]] = global i8 0, align 1 diff --git a/clang/test/CodeGen/asan-static-odr.cpp b/clang/test/CodeGen/asan-static-odr.cpp --- a/clang/test/CodeGen/asan-static-odr.cpp +++ b/clang/test/CodeGen/asan-static-odr.cpp @@ -11,7 +11,7 @@ // CHECK-NOT: __odr_asan_gen // CHECK-NOT: private alias -// CHECK: [[VAR:@.*global.*]] ={{.*}} global { i32, [28 x i8] } zeroinitializer, align 32 +// CHECK: [[VAR:@.*global.*]] ={{.*}} global { i32, [28 x i8] } zeroinitializer, sanitize_address, align 32 // CHECK: @0 = internal global {{.*}} [[VAR]] to i64), {{.*}}, i64 -1 }] // CHECK: call void @__asan_register_globals(i64 ptrtoint ([1 x { i64, i64, i64, i64, i64, i64, i64, i64 }]* @0 to i64), i64 1) // CHECK: call void @__asan_unregister_globals(i64 ptrtoint ([1 x { i64, i64, i64, i64, i64, i64, i64, i64 }]* @0 to i64), i64 1) diff --git a/clang/test/CodeGen/asan-strings.c b/clang/test/CodeGen/asan-strings.c --- a/clang/test/CodeGen/asan-strings.c +++ b/clang/test/CodeGen/asan-strings.c @@ -10,8 +10,8 @@ const char *foo(void) { return "asdf"; } -// LINUX: @.str = private unnamed_addr constant [5 x i8] c"asdf\00", align 1 +// LINUX: @.str = private unnamed_addr constant [5 x i8] c"asdf\00", sanitize_address, align 1 -// WINDOWS: @"??_C@_04JIHMPGLA@asdf?$AA@" = linkonce_odr dso_local unnamed_addr constant [5 x i8] c"asdf\00", comdat, align 1 +// WINDOWS: @"??_C@_04JIHMPGLA@asdf?$AA@" = linkonce_odr dso_local unnamed_addr constant [5 x i8] c"asdf\00", sanitize_address, comdat, align 1 -// WINWRITE: @.str = private unnamed_addr global [5 x i8] c"asdf\00", align 1 +// WINWRITE: @.str = private unnamed_addr global [5 x i8] c"asdf\00", sanitize_address, align 1 diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -746,6 +746,10 @@ [] [, section "name"] [, partition "name"] [, comdat [($name)]] [, align ] + [, no_sanitize] [, sanitize_address] + [, no_sanitize_address] [, sanitize_hwaddress] + [, no_sanitize_hwaddress] [, sanitize_memtag] + [, no_sanitize_memtag] [, sanitize_address_dyninit] (, !name !N)* For example, the following defines a global in a numbered address space @@ -1570,9 +1574,9 @@ Specify the desired alignment, which must be a power of two, in parentheses. ``"alloc-family"="FAMILY"`` - This indicates which "family" an allocator function is part of. To avoid - collisions, the family name should match the mangled name of the primary - allocator function, that is "malloc" for malloc/calloc/realloc/free, + This indicates which "family" an allocator function is part of. To avoid + collisions, the family name should match the mangled name of the primary + allocator function, that is "malloc" for malloc/calloc/realloc/free, "_Znwm" for ``::operator::new`` and ``::operator::delete``, and "_ZnwmSt11align_val_t" for aligned ``::operator::new`` and ``::operator::delete``. Matching malloc/realloc/free calls within a family @@ -20461,7 +20465,7 @@ The '``llvm.vp.fpext``' intrinsic extends the ``value`` from a smaller :ref:`floating-point ` type to a larger :ref:`floating-point ` type. The '``llvm.vp.fpext``' cannot be used to make a -*no-op cast* because it always changes bits. Use ``bitcast`` to make a +*no-op cast* because it always changes bits. Use ``bitcast`` to make a *no-op cast* for a floating-point cast. The conversion is performed on lane positions below the explicit vector length and where the vector mask is true. Masked-off lanes are undefined. diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -514,6 +514,7 @@ bool parseGlobalValueVector(SmallVectorImpl &Elts, Optional *InRangeOp = nullptr); bool parseOptionalComdat(StringRef GlobalName, Comdat *&C); + bool parseSanitizer(GlobalVariable *GV); bool parseMetadataAsValue(Value *&V, PerFunctionState &PFS); bool parseValueAsMetadata(Metadata *&MD, const Twine &TypeMsg, PerFunctionState *PFS); diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -423,7 +423,27 @@ Type, APFloat, // APFloatVal - APSInt // APSInt + APSInt, // APSInt + +// Extra sanitizer attributes that are used for global variables (GV's). Some +// of the attributes are inherited from Attributes.td, but we have some +// GV-specific additions. +#define DEFINED_IN_ATTRIBUTES_TD_NO_OP(var) + // GV's mentioned in -fsanitize-ignorelist=. + kw_no_sanitize, + DEFINED_IN_ATTRIBUTES_TD_NO_OP(kw_sanitize_address) + // GV's with __attribute__((no_sanitize("address"))). + kw_no_sanitize_address, + // GV's where the clang++ frontend (when ASan is used) notes that this is + // dynamically initialized, and thus needs ODR detection. + kw_sanitize_address_dyninit, + DEFINED_IN_ATTRIBUTES_TD_NO_OP(kw_sanitize_hwaddress) + // GV's with __attribute__((no_sanitize("hwaddress"))). + kw_no_sanitize_hwaddress, + DEFINED_IN_ATTRIBUTES_TD_NO_OP(kw_sanitize_memtag) + // GV's with __attribute__((no_sanitize("memtag"))). + kw_no_sanitize_memtag, + }; } // end namespace lltok } // end namespace llvm diff --git a/llvm/include/llvm/IR/GlobalValue.h b/llvm/include/llvm/IR/GlobalValue.h --- a/llvm/include/llvm/IR/GlobalValue.h +++ b/llvm/include/llvm/IR/GlobalValue.h @@ -79,14 +79,15 @@ ValueType(Ty), Visibility(DefaultVisibility), UnnamedAddrVal(unsigned(UnnamedAddr::None)), DllStorageClass(DefaultStorageClass), ThreadLocal(NotThreadLocal), - HasLLVMReservedName(false), IsDSOLocal(false), HasPartition(false) { + HasLLVMReservedName(false), IsDSOLocal(false), HasPartition(false), + HasSanitizerMetadata(false) { setLinkage(Linkage); setName(Name); } Type *ValueType; - static const unsigned GlobalValueSubClassDataBits = 16; + static const unsigned GlobalValueSubClassDataBits = 15; // All bitfields use unsigned as the underlying type so that MSVC will pack // them. @@ -111,9 +112,14 @@ /// https://lld.llvm.org/Partitions.html). unsigned HasPartition : 1; + /// True if this symbol has sanitizer metadata available. Should only happen + /// if sanitizers were enabled when building the translation unit which + /// contains this GV. + unsigned HasSanitizerMetadata : 1; + private: // Give subclasses access to what otherwise would be wasted padding. - // (16 + 4 + 2 + 2 + 2 + 3 + 1 + 1 + 1) == 32. + // (15 + 4 + 2 + 2 + 2 + 3 + 1 + 1 + 1 + 1) == 32. unsigned SubClassData : GlobalValueSubClassDataBits; friend class Constant; @@ -288,6 +294,47 @@ StringRef getPartition() const; void setPartition(StringRef Part); + struct SanitizerMetadata { + enum GlobalSanitizer : unsigned { + NoSanitize = 1 << 0, + Address = 1 << 1, + HWAddress = 1 << 2, + Memtag = 1 << 3, + NoAddress = 1 << 4, + NoHWAddress = 1 << 5, + NoMemtag = 1 << 6, + }; + // Bitset of sanitizer options. + std::underlying_type::type Sanitizer = 0; + + void AddSanitizer(GlobalSanitizer S) { Sanitizer |= S; } + void RemoveSanitizer(GlobalSanitizer S) { Sanitizer &= ~S; } + bool HasSanitizer(GlobalSanitizer S) const { + if (Sanitizer == GlobalSanitizer::NoSanitize) + return S == GlobalSanitizer::NoSanitize; + switch (S) { + case GlobalSanitizer::Address: + return (Sanitizer & S) && !(Sanitizer & GlobalSanitizer::NoAddress); + case GlobalSanitizer::HWAddress: + return (Sanitizer & S) && !(Sanitizer & GlobalSanitizer::NoHWAddress); + case GlobalSanitizer::Memtag: + return (Sanitizer & S) && !(Sanitizer & GlobalSanitizer::NoMemtag); + case GlobalSanitizer::NoSanitize: // clang doesn't see the handling above. + case GlobalSanitizer::NoAddress: + case GlobalSanitizer::NoHWAddress: + case GlobalSanitizer::NoMemtag: + return Sanitizer & S; + } + } + + // Metadata specific to ASan. + bool IsDynInit = false; + }; + + bool hasSanitizerMetadata() const { return HasSanitizerMetadata; } + const SanitizerMetadata &getSanitizerMetadata() const; + void setSanitizerMetadata(const SanitizerMetadata &Meta); + static LinkageTypes getLinkOnceLinkage(bool ODR) { return ODR ? LinkOnceODRLinkage : LinkOnceAnyLinkage; } diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -580,6 +580,18 @@ KEYWORD(prefix); KEYWORD(prologue); + // Sanitizer keywords. Some are inherited from Attributes.td. See LLToken.h + // for more information. +#define DEFINED_IN_ATTRIBUTES_TD_NO_OP(var) + KEYWORD(no_sanitize); + DEFINED_IN_ATTRIBUTES_TD_NO_OP(sanitize_address); + KEYWORD(no_sanitize_address); + KEYWORD(sanitize_address_dyninit); + DEFINED_IN_ATTRIBUTES_TD_NO_OP(sanitize_hwaddress); + KEYWORD(no_sanitize_hwaddress); + DEFINED_IN_ATTRIBUTES_TD_NO_OP(sanitize_memtag); + KEYWORD(no_sanitize_memtag); + KEYWORD(ccc); KEYWORD(fastcc); KEYWORD(coldcc); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -52,6 +52,9 @@ using namespace llvm; +using SanitizerMetadata = GlobalValue::SanitizerMetadata; +using GlobalSanitizer = SanitizerMetadata::GlobalSanitizer; + static std::string getTypeString(Type *T) { std::string Result; raw_string_ostream Tmp(Result); @@ -1103,6 +1106,65 @@ return false; } +static bool isSanitizer(lltok::Kind Kind) { + switch (Kind) { + case lltok::kw_no_sanitize: + case lltok::kw_sanitize_address: + case lltok::kw_no_sanitize_address: + case lltok::kw_sanitize_hwaddress: + case lltok::kw_no_sanitize_hwaddress: + case lltok::kw_sanitize_memtag: + case lltok::kw_no_sanitize_memtag: + case lltok::kw_sanitize_address_dyninit: + return true; + default: + return false; + } +} + +bool LLParser::parseSanitizer(GlobalVariable *GV) { + SanitizerMetadata Meta; + if (GV->hasSanitizerMetadata()) + Meta = GV->getSanitizerMetadata(); + + switch (Lex.getKind()) { + case lltok::kw_no_sanitize: + Meta.AddSanitizer(GlobalSanitizer::NoSanitize); + break; + case lltok::kw_sanitize_address: + Meta.AddSanitizer(GlobalSanitizer::Address); + break; + case lltok::kw_no_sanitize_address: + Meta.AddSanitizer(GlobalSanitizer::NoAddress); + break; + case lltok::kw_sanitize_hwaddress: + Meta.AddSanitizer(GlobalSanitizer::HWAddress); + break; + case lltok::kw_no_sanitize_hwaddress: + Meta.AddSanitizer(GlobalSanitizer::NoHWAddress); + break; + case lltok::kw_sanitize_memtag: + Meta.AddSanitizer(GlobalSanitizer::Memtag); + break; + case lltok::kw_no_sanitize_memtag: + Meta.AddSanitizer(GlobalSanitizer::NoMemtag); + break; + case lltok::kw_sanitize_address_dyninit: + if (!GV->hasSanitizerMetadata() || + !Meta.HasSanitizer(SanitizerMetadata::Address)) { + return tokError( + "sanitize_address_dyninit must only occur after sanitize_address"); + } + Meta.IsDynInit = true; + break; + default: + return tokError("non-sanitizer token passed to LLParser::parseSanitizer()"); + } + GV->setSanitizerMetadata(Meta); + Lex.Lex(); + return false; +} + /// parseGlobal /// ::= GlobalVar '=' OptionalLinkage OptionalPreemptionSpecifier /// OptionalVisibility OptionalDLLStorageClass @@ -1221,6 +1283,9 @@ } else if (Lex.getKind() == lltok::MetadataVar) { if (parseGlobalObjectMetadataAttachment(*GV)) return true; + } else if (isSanitizer(Lex.getKind())) { + if (parseSanitizer(GV)) + return true; } else { Comdat *C; if (parseOptionalComdat(Name, C)) diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -3540,6 +3540,15 @@ if (Record.size() > 15) NewGV->setPartition(StringRef(Strtab.data() + Record[14], Record[15])); + if (Record.size() > 17 && Record[16] != UINT_MAX && Record[17] != UINT_MAX) { + llvm::GlobalValue::SanitizerMetadata Meta; + Meta.Sanitizer = + static_cast( + Record[16]); + Meta.IsDynInit = static_cast(Record[17]); + NewGV->setSanitizerMetadata(Meta); + } + return Error::success(); } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1344,7 +1344,8 @@ // GLOBALVAR: [strtab offset, strtab size, type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat, attributes, DSO_Local] + // comdat, attributes, DSO_Local, GlobalSanitizer::Sanitizer, + // GlobalSanitizer::IsDynInit] Vals.push_back(addToStrtab(GV.getName())); Vals.push_back(GV.getName().size()); Vals.push_back(VE.getTypeID(GV.getValueType())); @@ -1360,10 +1361,8 @@ GV.getUnnamedAddr() != GlobalValue::UnnamedAddr::None || GV.isExternallyInitialized() || GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass || - GV.hasComdat() || - GV.hasAttributes() || - GV.isDSOLocal() || - GV.hasPartition()) { + GV.hasComdat() || GV.hasAttributes() || GV.isDSOLocal() || + GV.hasPartition() || GV.hasSanitizerMetadata()) { Vals.push_back(getEncodedVisibility(GV)); Vals.push_back(getEncodedThreadLocalMode(GV)); Vals.push_back(getEncodedUnnamedAddr(GV)); @@ -1377,6 +1376,15 @@ Vals.push_back(GV.isDSOLocal()); Vals.push_back(addToStrtab(GV.getPartition())); Vals.push_back(GV.getPartition().size()); + + if (!GV.hasSanitizerMetadata()) { + Vals.push_back(UINT_MAX); + Vals.push_back(UINT_MAX); + } else { + const auto &MD = GV.getSanitizerMetadata(); + Vals.push_back(static_cast(MD.Sanitizer)); + Vals.push_back(MD.IsDynInit); + } } else { AbbrevToUse = SimpleGVarAbbrev; } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -100,6 +100,9 @@ using UseListOrderMap = DenseMap>>; +using SanitizerMetadata = llvm::GlobalValue::SanitizerMetadata; +using GlobalSanitizer = SanitizerMetadata::GlobalSanitizer; + /// Look for a value that might be wrapped as metadata, e.g. a value in a /// metadata operand. Returns the input value as-is if it is not wrapped. static const Value *skipMetadataWrapper(const Value *V) { @@ -3537,6 +3540,28 @@ Out << '"'; } + if (GV->hasSanitizerMetadata()) { + SanitizerMetadata MD = GV->getSanitizerMetadata(); + if (MD.HasSanitizer(SanitizerMetadata::NoSanitize)) { + Out << ", no_sanitize"; + } else { + if (MD.HasSanitizer(SanitizerMetadata::Address)) + Out << ", sanitize_address"; + if (MD.HasSanitizer(SanitizerMetadata::HWAddress)) + Out << ", sanitize_hwaddress"; + if (MD.HasSanitizer(SanitizerMetadata::Memtag)) + Out << ", sanitize_address"; + if (MD.HasSanitizer(SanitizerMetadata::NoAddress)) + Out << ", no_sanitize_address"; + if (MD.HasSanitizer(SanitizerMetadata::NoHWAddress)) + Out << ", no_sanitize_hwaddress"; + if (MD.HasSanitizer(SanitizerMetadata::NoMemtag)) + Out << ", no_sanitize_memtag"; + if (MD.HasSanitizer(GlobalSanitizer::Address) && MD.IsDynInit) + Out << ", sanitize_address_dyninit"; + } + } + maybePrintComdat(Out, *GV); if (MaybeAlign A = GV->getAlign()) Out << ", align " << A->value(); diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp --- a/llvm/lib/IR/Globals.cpp +++ b/llvm/lib/IR/Globals.cpp @@ -67,6 +67,8 @@ setDLLStorageClass(Src->getDLLStorageClass()); setDSOLocal(Src->isDSOLocal()); setPartition(Src->getPartition()); + if (Src->hasSanitizerMetadata()) + setSanitizerMetadata(Src->getSanitizerMetadata()); } void GlobalValue::removeFromParent() { @@ -217,6 +219,18 @@ HasPartition = !S.empty(); } +using SanitizerMetadata = GlobalValue::SanitizerMetadata; +using GlobalSanitizer = GlobalValue::SanitizerMetadata::GlobalSanitizer; +const SanitizerMetadata &GlobalValue::getSanitizerMetadata() const { + assert(hasSanitizerMetadata()); + return getContext().pImpl->GlobalValueSanitizerMetadata[this]; +} + +void GlobalValue::setSanitizerMetadata(const SanitizerMetadata &Meta) { + getContext().pImpl->GlobalValueSanitizerMetadata[this] = Meta; + HasSanitizerMetadata = true; +} + StringRef GlobalObject::getSectionImpl() const { assert(hasSection()); return getContext().pImpl->GlobalObjectSections[this]; diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1503,6 +1503,9 @@ /// Collection of per-GlobalValue partitions used in this context. DenseMap GlobalValuePartitions; + DenseMap + GlobalValueSanitizerMetadata; + /// DiscriminatorTable - This table maps file:line locations to an /// integer representing the next DWARF path discriminator to assign to /// instructions in different blocks at the same location.