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,6 +14,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" namespace llvm { @@ -40,7 +41,8 @@ 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); + StringRef Name, QualType Ty = {}, bool IsDynInit = false, + SanitizerMask NoSanitizeMask = {}); void disableSanitizerForGlobal(llvm::GlobalVariable *GV); void disableSanitizerForInstruction(llvm::Instruction *I); 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,27 +17,80 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/Constants.h" +using GlobalVariable = llvm::GlobalVariable; +using GVSanitizerMetadata = GlobalVariable::SanitizerMetadata; + using namespace clang; using namespace CodeGen; SanitizerMetadata::SanitizerMetadata(CodeGenModule &CGM) : CGM(CGM) {} +// TODO(hctim): Can be removed when we migrate off of llvm.asan.globals. This +// prevents llvm.asan.globals from being emitted for +// __attribute__((disable_sanitizer_instrumentation)) and uses of +// -fsanitize-ignorelist when a sanitizer isn't enabled. static bool isAsanHwasanOrMemTag(const SanitizerSet &SS) { return SS.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress | SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress | SanitizerKind::MemTag); } -void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV, - SourceLocation Loc, StringRef Name, - QualType Ty, bool IsDynInit, - bool IsExcluded) { - IsDynInit &= !CGM.isInNoSanitizeList(GV, Loc, Ty, "init"); - IsExcluded |= CGM.isInNoSanitizeList(GV, Loc, Ty); +void SanitizerMetadata::reportGlobal(GlobalVariable *GV, SourceLocation Loc, + StringRef Name, QualType Ty, + bool IsDynInit, + SanitizerMask NoSanitizeMask) { + + GVSanitizerMetadata Meta; + if (GV->hasSanitizerMetadata()) + Meta = GV->getSanitizerMetadata(); + + auto &SanTarget = CGM.getLangOpts().Sanitize; + + if (!isAsanHwasanOrMemTag(SanTarget)) + return; + + SanitizerSet NoSanitizeSet; + NoSanitizeSet.Mask = NoSanitizeMask; + + bool IsExcluded = CGM.isInNoSanitizeList(GV, Loc, Ty); + IsExcluded |= (NoSanitizeMask == SanitizerKind::All); + + if (SanTarget.hasOneOf(SanitizerKind::Address | + SanitizerKind::KernelAddress)) { + IsExcluded |= CGM.isInNoSanitizeList(GV, Loc, Ty, "address"); + IsExcluded |= NoSanitizeSet.hasOneOf(SanitizerKind::Address | + SanitizerKind::KernelAddress); + IsExcluded |= Meta.NoAddress; + Meta.NoAddress = IsExcluded; + + // TODO(hctim): Make this conditional when we migrate off llvm.asan.globals. + IsDynInit &= !CGM.isInNoSanitizeList(GV, Loc, Ty, "init"); + Meta.IsDynInit = IsDynInit; + } + + if (SanTarget.hasOneOf(SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress)) { + IsExcluded |= CGM.isInNoSanitizeList(GV, Loc, Ty, "hwaddress"); + IsExcluded |= NoSanitizeSet.hasOneOf(SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress); + IsExcluded |= Meta.NoHWAddress; + Meta.NoHWAddress = IsExcluded; + } + + if (SanTarget.hasOneOf(SanitizerKind::MemTag)) { + IsExcluded |= CGM.isInNoSanitizeList(GV, Loc, Ty, "memtag"); + IsExcluded |= NoSanitizeSet.hasOneOf(SanitizerKind::MemTag); + IsExcluded |= Meta.NoMemtag; + Meta.NoMemtag = IsExcluded; + } + + GV->setSanitizerMetadata(Meta); + // TODO(hctim): Code below can be removed when we migrate off of + // llvm.asan.globals onto the new metadata attributes. + llvm::LLVMContext &VMContext = CGM.getLLVMContext(); 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,8 +101,8 @@ 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))}; @@ -59,41 +112,27 @@ AsanGlobals->addOperand(ThisGlobal); } -void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV, const VarDecl &D, +void SanitizerMetadata::reportGlobal(GlobalVariable *GV, const VarDecl &D, bool IsDynInit) { - if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) - return; + 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); - auto getNoSanitizeMask = [](const VarDecl &D) { - if (D.hasAttr()) - return SanitizerKind::All; - - SanitizerMask NoSanitizeMask; - for (auto *Attr : D.specific_attrs()) - NoSanitizeMask |= Attr->getMask(); - - return NoSanitizeMask; - }; reportGlobal(GV, D.getLocation(), OS.str(), D.getType(), IsDynInit, - SanitizerSet{getNoSanitizeMask(D)}.has(SanitizerKind::Address)); -} - -void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV, - SourceLocation Loc, StringRef Name, - QualType Ty, bool IsDynInit) { - if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) - return; - reportGlobal(GV, Loc, Name, Ty, IsDynInit, false); + 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)) - reportGlobal(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/Inputs/sanitizer-special-case-list-globals.txt b/clang/test/CodeGen/Inputs/sanitizer-special-case-list-globals.txt new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/Inputs/sanitizer-special-case-list-globals.txt @@ -0,0 +1,7 @@ +global:always_ignored +[address] +global:asan_ignored +[hwaddress] +global:hwasan_ignored +[memtag] +global:memtag_ignored diff --git a/clang/test/CodeGen/asan-globals.cpp b/clang/test/CodeGen/asan-globals.cpp --- a/clang/test/CodeGen/asan-globals.cpp +++ b/clang/test/CodeGen/asan-globals.cpp @@ -23,6 +23,9 @@ const char *literal = "Hello, world!"; } +// ASAN: @dyn_init_global = global {{.*}}, sanitize_address_dyninit +// KASAN: @dyn_init_global = global {{.*}}, sanitize_address_dyninit + // ASAN: sectioned_global{{.*}} global { i32, [28 x i8] }{{.*}}, align 32 // KASAN: sectioned_global{{.*}} global i32 // ASAN: @__special_global{{.*}} global { i32, [28 x i8] }{{.*}}, align 32 diff --git a/clang/test/CodeGen/sanitize-init-order.cpp b/clang/test/CodeGen/sanitize-init-order.cpp --- a/clang/test/CodeGen/sanitize-init-order.cpp +++ b/clang/test/CodeGen/sanitize-init-order.cpp @@ -36,12 +36,29 @@ // Check that ASan init-order checking ignores structs with trivial default // constructor. + +// CHECK: @s1 = global +// CHECK-NOT: sanitize_address_dyninit +// CHECK: @s2 = global +// CHECK-NOT: sanitize_address_dyninit +// CHECK: @s3 = global {{.*}}, sanitize_address_dyninit +// CHECK: @{{.*}}array{{.*}} = global {{.*}}, sanitize_address_dyninit + // CHECK: !llvm.asan.globals = !{![[GLOB_1:[0-9]+]], ![[GLOB_2:[0-9]+]], ![[GLOB_3:[0-9]+]], ![[GLOB_4:[0-9]+]] // CHECK: ![[GLOB_1]] = !{%struct.PODStruct* {{.*}}, i1 false, i1 false} // CHECK: ![[GLOB_2]] = !{%struct.PODWithDtor* {{.*}}, i1 false, i1 false} // CHECK: ![[GLOB_3]] = !{%struct.PODWithCtorAndDtor* {{.*}}, i1 true, i1 false} // CHECK: ![[GLOB_4]] = !{{{.*}}class.NS::PODWithCtor{{.*}}, i1 true, i1 false} +// IGNORELIST: @s1 = global +// IGNORELIST-NOT: sanitize_address_dyninit +// IGNORELIST: @s2 = global +// IGNORELIST-NOT: sanitize_address_dyninit +// IGNORELIST: @s3 = global +// IGNORELIST-NOT: sanitize_address_dyninit +// IGNORELIST: @{{.*}}array{{.*}} = global +// IGNORELIST-NOT: sanitize_address_dyninit + // IGNORELIST: !llvm.asan.globals = !{![[GLOB_1:[0-9]+]], ![[GLOB_2:[0-9]+]], ![[GLOB_3:[0-9]+]], ![[GLOB_4:[0-9]+]]} // IGNORELIST: ![[GLOB_1]] = !{%struct.PODStruct* {{.*}}, i1 false, i1 false} // IGNORELIST: ![[GLOB_2]] = !{%struct.PODWithDtor* {{.*}}, i1 false, i1 false} diff --git a/clang/test/CodeGen/sanitizer-special-case-list-globals.c b/clang/test/CodeGen/sanitizer-special-case-list-globals.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/sanitizer-special-case-list-globals.c @@ -0,0 +1,55 @@ +// Verify that ignorelist sections correctly select sanitizers to apply +// ignorelist entries to. + +// RUN: %clang_cc1 -fsanitize=address -emit-llvm %s -o -\ +// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \ +// RUN: | FileCheck %s --check-prefix=ASAN + +// Note: HWASan effectively reorders globals (it puts the unsanitized ones +// first), which is hard to check for, as 'CHECK-DAG' doesn't play terribly +// nicely with 'CHECK-NOT'. This is why the 'always_ignored' and +// 'hwasan_ignored' comes first in this file. +// RUN: %clang_cc1 -fsanitize=hwaddress -emit-llvm %s -o -\ +// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \ +// RUN: | FileCheck %s --check-prefix=HWASAN + +// TODO(hctim): Move over to memtag-globals when it's implemented. For now +// though, it's fine, the frontend still annotates based on any memtag sanitizer +// being used. +// RUN: %clang_cc1 -fsanitize=memtag-heap -triple=aarch64-linux-android31 -emit-llvm %s -o -\ +// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \ +// RUN: | FileCheck %s --check-prefix=MEMTAG + +// ASAN: @always_ignored = global {{.*}}, no_sanitize_address +// HWASAN: @always_ignored = global {{.*}}, no_sanitize_hwaddress +// MEMTAG: @always_ignored = global {{.*}}, no_sanitize_memtag +unsigned always_ignored; + +// ASAN: @hwasan_ignored = global +// ASAN-NOT: no_sanitize_address +// HWASAN: @hwasan_ignored = global {{.*}}, no_sanitize_hwaddress +// MEMTAG: @hwasan_ignored = global +// MEMTAG-NOT: no_sanitize_memtag +unsigned hwasan_ignored; + +// ASAN: @asan_ignored = global {{.*}}, no_sanitize_address +// HWASAN: @asan_ignored.hwasan = {{.*}} global +// HWASAN-NOT: no_sanitize_hwaddress +// MEMTAG: @asan_ignored = global +// MEMTAG-NOT: no_sanitize_memtag +unsigned asan_ignored; + +// ASAN: @memtag_ignored = global +// ASAN-NOT: no_sanitize_address +// HWASAN: @memtag_ignored.hwasan = {{.*}} global +// HWASAN-NOT: no_sanitize_hwaddress +// MEMTAG: @memtag_ignored = global {{.*}}, no_sanitize_memtag +unsigned memtag_ignored; + +// ASAN: @never_ignored = global +// ASAN-NOT: no_sanitize_address +// HWASAN: @never_ignored.hwasan = {{.*}} global +// HWASAN-NOT: no_sanitize_hwaddress +// MEMTAG: @never_ignored = global +// MEMTAG-NOT: no_sanitize_memtag +unsigned never_ignored;